首页 > 其他分享 >Codeforces Round 930 (Div. 2)

Codeforces Round 930 (Div. 2)

时间:2024-03-03 10:45:02浏览次数:15  
标签:std cnt kN int sum Codeforces cin Div 930

目录

写在前面

比赛地址:https://codeforces.com/contest/1937

被交互杀死的一集。

赛时卡 C 明天有早八就直接睡大觉了,第二天看看 D 一眼秒了,最困的一集。

A

签到

发现 1 会被先后交换到 2,4,8,16……

输出 \(2^{\left\lfloor\log n \right\rfloor}\) 即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    int a; std::cin >> a;
    std::cout << (1ll << ((int) log2(a))) << "\n";
  }
  return 0;
}

B

贪心。

只有两行。构造字典序最小的字符串 \(t\) 时贪心即可,考虑在当前位置是往右走更优还是转移到第二行更优。

输出方案即考虑找到所有的位置 \(i\),满足 \(a_1[1:i]\) 与 \(t[1:i]\) 相同,\(a_2[i:n]\) 与 \(t[i + 1:n + 1]\) 相同,于是预处理第一行每个位置可以匹配到 \(t\) 的前几位,第二行每个位置可以匹配到 \(t\) 的后几位,然后枚举 \(i\) 检查即可。

//贪心。
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, yes[2][kN];
std::string s[2], t;
//=============================================================
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n;
    std::cin >> s[0];
    std::cin >> s[1];

    t.clear();
    t.push_back(s[0][0]);
    int now = 0;
    for (int i = 0; i < n; ++ i) {
      if (now == 1 || i == n - 1) t.push_back(s[1][i]);
      else {
        if (s[1][i] < s[0][i + 1]) now = 1, t.push_back(s[1][i]);
        else t.push_back(s[0][i + 1]);
      }
    }
    std::cout << t << "\n";

    int ans = 0;
    for (int i = 0; i < n; ++ i) yes[0][i] = yes[1][i] = 0;
    for (int i = 0; i < n; ++ i) {
      if (s[0][i] != t[i]) break;
      yes[0][i] = 1;
    }
    for (int i = n - 1, j = n; i >= 0; -- i, -- j) {
      if (s[1][i] != t[j]) break;
      yes[1][i] = 1;
    }
    for (int i = 0; i < n; ++ i) {
      if (yes[0][i] && yes[1][i]) ++ ans;
    }
    std::cout << ans << "\n";
  }
  return 0;
}

C

交互,位运算

妈的我没脑子

暴力打个表发现两个数异或的最大值为 \(2^{\left\lfloor\log n - 1 \right\rfloor + 1} - 1\),可以且仅可以由一对和为 \(2^{\left\lfloor\log n - 1 \right\rfloor + 1} - 1\) 的数异或得到。

找到最大和最小值的位置是很容易的。以最大值为例,仅需记录当前最大值位置 \(x\),对 \(i(1\le i< n)\) 进行询问 ? x x i i 并更新 \(x\) 即可。

于是考虑先求得最大值 \(n-1\) 的位置 \(x\),再找到 \(2^{\left\lfloor\log n - 1 \right\rfloor + 1} - 1 - (n - 1)\) 来凑出最大值。发现这个数满足如下特征:

  • 由异或的性质可知该数与 \(n-1\) 各二进制位上的值是互补的。
  • 该数与 \(n-1\) 的或也为 \(2^{\left\lfloor\log n - 1 \right\rfloor + 1} - 1\)。
  • 在所有与 \(n-1\) 的或为 \(2^{\left\lfloor\log n - 1 \right\rfloor + 1} - 1\) 的数中,该数是最小的(二进制位上的 0 最多)。

又所有数与 \(n-1\) 的或的最大值为 \(2^{\left\lfloor\log n - 1 \right\rfloor + 1} - 1\),于是记当前与 \(n-1\) 或最大的位置集合为 \(s\),考虑对 \(i(1\le i< n)\) 进行询问 ? x i x s[0],若大于则令 \(s= \{ i \}\),若等于则将 \(i\) 加入 \(s\),即可得到所有与 \(n-1\) 的或的最大值为 \(2^{\left\lfloor\log n - 1 \right\rfloor + 1} - 1\) 的数。

然后对集合 \(s\) 中的位置运行上述求最小值的算法,即可得到要求的数的位置 \(y\),输出 ! x y 即可。

上述三个过程均仅需要 \(n-1\) 次询问,总询问次数合法。

//交互,位运算
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
int n, ans1, ans2;
//=============================================================
char query(int a_, int b_, int c_, int d_) {
  std::cout << "? " << a_ << " " << b_ << " " << c_ << " " << d_ << "\n";
  fflush(stdout);
  char ret; 
  std::cin >> ret;
  return ret;
}
void Solve() {
  std::cin >> n;
  ans1 = ans2 = 0;
  for (int i = 1; i < n; ++ i) {
    char ret = query(i, i, ans1, ans1);
    if (ret == '>') ans1 = i;
  }

  int maxp = 0;
  std::vector <int> pos;
  pos.push_back(0);
  for (int i = 1; i < n; ++ i) {
    if (i == ans1) continue;
    char ret = query(ans1, i, ans1, maxp);
    if (ret == '>') {
      maxp = i;
      pos.clear(), pos.push_back(maxp);
    } else if (ret == '=') {
      pos.push_back(i);
    }
  }
  
  ans2 = maxp;
  for (auto x: pos) {
    int ret = query(x, x, ans2, ans2);
    if (ret == '<') ans2 = x;
  }
  std::cout << "! " << ans1 << " " << ans2 << "\n";
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  // std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    Solve();
  }
  return 0;
}

D

前缀和,二分

甚至没什么好说的题呃呃。

先手玩,发现对于一段长为 \(d\) 的 >>...>><,若小球从左侧进入,则会先运动到最右侧且字符串变为 <<...<<<,然后小球将在转向后重新回到起点,字符串变为 >>...>>>。相当于消去了与初始方向相反的第一个位置,移动了 \(2\times d\) 的距离。

则对于某个起点,小球的运动路径是左右反复横跳,仅需考虑左侧的 > 与右侧的 < 的贡献,且每个符号的贡献为它们与起点的距离。于是考虑前缀和维护各个符号的数量与坐标之和,然后枚举起点,先分别求得两侧 >< 的数量并比较确定在哪侧移出平面,则答案可以分成三份:

  • 这一侧所有符号,它们的贡献都可以取到,前缀和统计即可。
  • 另一侧的前若干的符号,数量大于这一侧符号数量,可以通过二分得到其位置,前缀和可得到其区间贡献。
  • 在这一侧最后一次移出平面的移动,贡献即为起点与端点的距离。

实现时需要注意端点。我的写法中求两侧的符号数量时左侧范围是 \(1\sim i-1\),右侧是 \(i\sim n\)(相当于钦定了小球初始向右移动,即不考虑第 \(i\) 个位置为 > 的影响),则当右侧大于左侧时终点为左侧,否则为右侧。

总时间复杂度 \(O(n\log n)\) 级别。

//前缀和,二分
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 5e5 + 10;
//=============================================================
int n;
std::string s;
LL cnt[2][kN], sum[2][kN];
//=============================================================
void Init() {
  std::cin >> n;
  std::cin >> s;
  for (int i = 1; i <= n; ++ i) {
    int t = (s[i - 1] == '>');
    cnt[t][i] = cnt[t][i - 1] + 1, cnt[t ^ 1][i] = cnt[t ^ 1][i - 1];
    sum[t][i] = sum[t][i - 1] + i, sum[t ^ 1][i] = sum[t ^ 1][i - 1];
  }
}
void Solve() {
  for (int i = 1; i <= n; ++ i) {
    LL ans = 0;
    int cl = cnt[1][i - 1], cr = cnt[0][n] - cnt[0][i - 1];
    if (cr > cl) {
      int p = i;
      for (int l = i, r = n; l <= r; ) {
        int mid = (l + r) >> 1;
        if (cnt[0][mid] - cnt[0][i - 1] > cl) {
          p = mid;
          r = mid - 1;
        } else {
          l = mid + 1;
        }
      }
      ans = 2ll * (1ll * cl * i - sum[1][i - 1]) 
            + 2ll * (1ll * sum[0][p] - sum[0][i - 1] - 1ll * (cnt[0][p] - cnt[0][i - 1]) * i) + i;
    } else {
      int p = i;
      for (int l = 1, r = i; l <= r; ) {
        int mid = (l + r) >> 1;
        if (cnt[1][i - 1] - cnt[1][mid - 1] >= cr) {
          p = mid;
          l = mid + 1;
        } else {
          r = mid - 1;
        }
      }
      ans = 2ll * (1ll * (cnt[1][i - 1] - cnt[1][p - 1]) * i 
                  - (sum[1][i - 1] - sum[1][p - 1]))
            + 2ll * (1ll * sum[0][n] - sum[0][i - 1] - 1ll * cr * i) + n - i + 1;
    }
    std::cout << ans << " ";
  }
  std::cout << "\n";
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    Init();
    Solve();
  }
  return 0;
}

E

建图,最短路

居然是牛逼建图最短路,而且这个图建得有一种自动机的味儿,学到了。

首先对于某属性强化操作的终点是离散的,一定是某个宝可梦该属性值,

经过强化后宝可梦的属性是递增的,且召唤完之后还需要把它打败,则所有宝可梦至多只会召唤一次,若出现召唤多次的情况说明有一段操作是冗余甚至有害的(使当前被召唤多次的宝可梦的属性上升从而不好打败了)。

考虑最优的操作序列的形态:(加强 \(a_1\) 直至能打败 1,召唤 \(a_1\) 打败 1) \(\longrightarrow\) (加强 \(a_2\) 直至能打败 \(a_1\),召唤 \(a_2\) 打败 \(a_1\)) \(\longrightarrow \cdots \longrightarrow\) (加强 \(a_n\) 直至能打败 \(a_k\),召唤 \(n\) 打败 \(a_k\))。

考虑把这个操作序列倒过来考虑,如果把当前在道馆的宝可梦看做结点,问题好像变为找到一条 \(n\rightarrow 1\) 的最短的有向路径,有向边代表前一个宝可梦打败后一个,于是考虑建图跑最短路:

  • 对于每个宝可梦 \(i\) 的属性 \(j\),建立节点 \(X_{i, j}\),代表召唤该宝可梦后准备使用其属性 \(j\);建立 \(Y_{i, j}\) 表示可以打败该宝可梦。
  • 连有向边 \((i, X_{i, j}, c_i)\) 表示召唤,\((X_{i, j}, Y_{i, j}, 0)\) 表示召唤自身,\((Y_{i, j}, i, 0)\) 表示打败该宝可梦后使该宝可梦可用来召唤。
  • 对于每种属性,将所有宝可梦按照该属性值升序排序,设该属性为 \(j\) 且排序后顺序为 \(b_{1}\sim b_n\),则连边 \((X_{b_{i-1}, j}, X_{b_{i}, j}, a_{b_{i}, j} - a_{b_{i - 1}, j})\) 表示强化操作至第一个大于的宝可梦,连边 \((Y_{b_{i}, j}, Y_{b_{i - 1}, j}, 0)\) 表示可以打败更弱的宝可梦。

然后跑出 \(n\rightarrow 1\) 的最短路即为答案。

节点与边数均为 \(O(nm)\) 级别,总时间复杂度 \(O(nm\log {nm})\) 级别。

//知识点:建图,最短路
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
#define pr std::pair
#define mp std::make_pair
const int kN = 2e6 + 10;
const LL kInf = 1e18 + 2077;
//=============================================================
int n, m, nodenum;
int edgenum, head[kN], v[kN << 1], w[kN << 1], ne[kN << 1];
int c[kN];
std::vector <std::vector <pr <int, int> > > a;
LL dis[kN];
bool vis[kN];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
void Add(int u_, int v_, int w_) {
  v[++ edgenum] = v_;
  w[edgenum] = w_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
void Dijkstra() {
  std::priority_queue <pr <LL, int> > q;
  for (int i = 1; i <= nodenum; ++ i) dis[i] = kInf, vis[i] = 0;
  q.push(mp(0, n));
  dis[n] = 0;
  
  while (!q.empty()) {
    int u_ = q.top().second; q.pop();
    if (vis[u_]) continue;
    vis[u_] = true;
    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i], w_ = w[i];
      if (dis[u_] + w_ < dis[v_]) {
        dis[v_] = dis[u_] + w_;
        q.push(mp(-dis[v_], v_));
      }
    }
  }
}
void Init() {
  for (int i = 1; i <= nodenum; ++ i) {
    head[i] = 0;
  }
  nodenum = edgenum = 0;
  
  nodenum = n = read(), m = read();
  for (int i = 1; i <= n; ++ i) c[i] = read();
  a.clear(), a.push_back(std::vector <pr <int, int> >());
  for (int i = 1; i <= m; ++ i) {
    a.push_back(std::vector <pr <int, int> >(1, mp(0, 0)));
  }
  for (int i = 1; i <= n; ++ i) {
    for (int j = 1; j <= m; ++ j) {
      a[j].push_back(mp(read(), i));
    }
  }

  for (int i = 1; i <= m; ++ i) {
    std::sort(a[i].begin(), a[i].end());
    for (int j = 1; j <= n; ++ j) {
      int x = ++ nodenum;
      int y = ++ nodenum;
      Add(x, y, 0);
      Add(a[i][j].second, x, c[a[i][j].second]);
      Add(y, a[i][j].second, 0);
      if (j != 1) Add(x - 2, x, a[i][j].first - a[i][j - 1].first);
      if (j != 1) Add(y, y - 2, 0);
    }
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  int T = read();
  while (T --) {
    Init();  
    // for (int i = 1; i <= nodenum; ++ i) {
    //   for (int j = head[i]; j; j = ne[j]) {
    //     printf("%d %d %d\n", i, v[j], w[j]);
    //   }
    // }
    Dijkstra();
    printf("%lld\n", dis[1]);
  }
  return 0;
}
/*
1
3 1
2
6
1
*/

F

看着像数据结构,咕咕

写在最后

学到了什么:

  • C:有点感觉但也不知道具体学到了啥。
  • D:注意边界。
  • E:有操作序列的最小化问题考虑用结点表示状态,往自动机方向思考,可能在自动机上跑贪心或者最短路。

标签:std,cnt,kN,int,sum,Codeforces,cin,Div,930
From: https://www.cnblogs.com/luckyblock/p/18049666

相关文章

  • D - Diversity of Scores
    D-DiversityofScoreshttps://atcoder.jp/contests/abc343/tasks/abc343_d 思路准备两个map第一个存储,每个分数作为key,以及得此分数的运动员列表作为value这样,可以非常快速统计出某一时刻所有分数总数。第二个存储,每个运动员作为key,以及此运动员当前的分......
  • Codeforces Round 931 (Div. 2) A-D2
    A.TooMinTooMax贪心、排序。对数组排序后,显然当下标\(i\)、\(j\)、\(k\)、\(l\)分别选择\(1\)、\(n\)、\(2\)、\(n-1\)时求得最大值。时间复杂度:\(O(nlogn)\)。#include<bits/stdc++.h>usingnamespacestd;#definecctieios::sync_with_stdio(0);cin.tie(0)......
  • Codeforces Round 926 (Div. 2)
    A-SashaandtheBeautifulArray难度:⭐题目大意给定一个长度为n的数组,其分数为An-An-1+An-1-An-2...+A2-A1;问如何排列可以让该数组的分数最大;解题思路就是让An-A1最大;神秘代码#include<bits/stdc++.h>#defineintlonglong#defineIOSio......
  • Codeforces Round 931 (Div. 2)
    CodeforcesRound931(Div.2)比赛链接A.TooMinTooMax思路这题一开始我以为就是简单的模拟一下,四个for循环可能就可以,事实上并不是,因为我们想让最后的值最大,所以我们可以将数组进行排序,之后我们从最左边取两个,最右边取两个,插着求绝对值的和就行Code#include<bits/stdc......
  • Codeforces Round 911 (Div. 2) vp D题
    前面三题,就B题一道900的题目让我wa了两发,而且有点难看出来主要是想不到。。不知道该怎么像。应该算是题目理解不清晰吧,很麻烦。。这个原因可以说是没有一个完整的思考路径,我能够直接想到答案接近的处理方法,但是细节上没有注意。在考虑问题的时候可能。。需要慢一些,太快了,容易漏......
  • CodeForces 1936D Bitwise Paradox
    洛谷传送门CF传送门和CF1004FSonyaandBitwiseOR很像。考虑一次询问怎么做。考虑分治,每次只计算左端点在\([l,mid]\),右端点在\([mid+1,r]\)的区间的贡献。对于每个\(i\in[l,mid]\),维护最小的\(j\in[mid+1,r]\)使得\([i,j]\)的或\(\gev\),那么\(\m......
  • Codeforces 839E Mother of Dragons
    令\(s_u\)为点\(u\)分配到的权值。结论:最后选出来有权值的肯定是一个最大团。考虑反证,如果\(x,y\)间没有连边,则\(x,y\)的贡献是独立的,若\(\sum\limits_{(u,x)\inE}s_u\ge\sum\limits_{(v,y)\inE}s_v\),那么就可以把\(s_y\)给\(s_x\),否则把\(s_x\)给\(s_......
  • div3笔记
     Problem-E-Codeforces这道题用了记录一个数末尾零的板子(敲重点)!!!再说一遍,简单博弈论就是贪心!1voidsolve(){2cin>>n>>m;3vector<int>a(n),b(n);4for(inti=0;i<n;i++)cin>>a[i];5intlen=0;//这组数字总共有几位,总长度6......
  • 2024 蓝桥杯模拟赛3(div1+div2)
    2024蓝桥杯模拟赛3(div1+div2)P8834[传智杯#3决赛]序列简单的模拟,数据范围很小,暴力即可点击查看代码#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=1e5+5;voidsolve(){ lln,k,a[N],cnt=0; cin>>n>>k; for(inti=1;i<=n;i++)c......
  • vue项目引入高德地图报错:Map container div not exist (火狐浏览器不加载地图)
    问题描述:谷歌浏览器正常显示地图,火狐浏览器不加载,并且报错:  Mapcontainerdivnotexist错误代码如下:  修改后代码如下:  参考大佬:https://blog.csdn.net/white_777/article/details/128286558  ......