CodeForces - 1632D New Year Concert(ST 表、二分)

D - New Year Concert

题目大意:

给出一个序列 a

对于一个序列 b,如果其中存在一个区间 [L,R],满足区间 gcd 等于区间长度 RL+1,则认为这个序列是不好的。

每次修改可以将任意一个数改成任意正整数。

现求对序列 a 的每一个前缀最少需要修改多少次,使得该前缀是一个好序列。

思路:

通过简单模拟一下样例 3,我们可以发现。

  • f(x) 的状态继承了 f(x1) 的状态。并且假设每次答案最多增加 1(但还是不太懂为什么)
  • 对于一个区间 [l,r],其区间 gcd 满足单调递增。
  • 为了让修改次数最少,我们可以每次将数修改为一个大质数,因为区间 gcd 单调递增,那么修改成大质数的话,我们就不用考虑这个质数所在位置及其前面的数字了,他们与区间右端点的 gcd 都为 1,而要想满足是不好序列的条件只用检查区间端点。

由于我们并不需要真的去修改一个数,我们可以用 ST 表预处理出区间 gcd

那么,现在问题就在于当我们考虑以 i 位置为结尾的前缀时,我们如何求出 [1,i] 这段区间上我们要修改几次。

前面说到 f(x) 的状态可以由 f(x1) 的状态转移得到,并且我们在 x 之前可能会进行一些修改操作,将某些数字改成大质数,那么我们可以用变量 idx 记录上一次修改的位置,问题就由转化成如何求出在 [idx,i] 这段区间上我们要修改几次。

由单调性考虑二分,二分找是否存在一个位置 p ,使得 [p,i] 是一个不合法区间。

tmp 为区间 [mid,i]gcdlen[mid,i] 的区间长度。

因为 tmp 单调递增,len 单调递减,那么一定会出现以下的局面。

Copy
----------------|*|------------------*> tmp < len tmp > len i 不合法

这样我们就能二分的找是否存在这个位置 p

Code:
Copy
class SparseTable { public: int lg[N] = {-1}; ll st[24][N]; template <class T> T op(T &a, T &b) { return gcd(a, b); } //检查区间操作! SparseTable() { for (int i = 1; i < N; i++) { lg[i] = lg[i / 2] + 1; } } inline void init(int n, vector<ll> &a) { //完成初始化! for i in [1, n]: st[0][i] = val[i], 对应区间 [i, i + 2^0 -1] for (int i = 1, x; i <= n; i++) { st[0][i] = a[i]; } build(n); } inline void build(int n) { for (int i = 1; i <= lg[n]; i++) for (int j = 1; j + (1 << i) - 1 <= n; j++) st[i][j] = op(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]); } ll query(int l, int r) { ll len = lg[r - l + 1]; return op(st[len][l], st[len][r - (1 << len) + 1]); } }; SparseTable ST; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<ll> a(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } ST.init(n, a); ll ans = 0; int idx = 1; // 将a[idx - 1]修改为大质数 for (int i = 1; i <= n; i++) { if (a[i] == 1) { ans++; idx = i + 1; } else { int l = idx, r = i; // 二分找是否存在一个位置p,使得[p, i]是一个bored区间 bool cur = false; while (l <= r) { int mid = (l + r) >> 1; int tmp = ST.query(mid, i); int len = i - mid + 1; if (tmp < len) { l = mid + 1; } else if (tmp == len) { cur = true; break; } else { r = mid - 1; } } if (cur) { ans++; idx = i + 1; } } cout << ans << " \n"[i == n]; } return 0; }
posted @   Nepenthe8  阅读(77)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示