CF1632 D. New Year Concert
题意:
给定数组。对一个区间 \([l,r]\),若其中所有数的 gcd 恰等于区间长度,则这个区间是 bad 的;否则是 good 的。
每次操作可以修改数组中的一个数,改为任意整数。
定义 \(f(i)\) 为使 \([1,i]\) 的所有子区间都 good 的最小操作数。求 \(f(1),f(2),\cdots ,f(n)\)
思路:
首先找出所有 bad 区间:
对于固定的 \(l\),随着 \(r\) 增加,\(\gcd(a_l,a_{l+1},\cdots ,a_r)\) 递减,区间长度 \(r-l+1\) 递增。所以可以二分+ST表求出所有 bad 区间。(听说也可以双指针、线段树之类的)
怎样把一个 bad 区间改成 good 的?只需把其中的任一数改成一个超大的质数。假如改了 \(a_i\),那么所有包含 \(a_i\) 的区间都将 good
然后变成一个经典贪心:
选择最少的点,使每个区间至少包含一个点。按右端点排序然后每次贪心选右端点即可。
(还可以发现这些区间是不会互相包含的,但不知道好像也没关系)
int erfen(int x) { //找 gcd(a[x],a[x+1],...,a[y]) >= y-x+1 的最大y
int l = x, r = n;
while(l < r) {
int mid = (ll)l + r + 1 >> 1;
if(ask(x,mid) >= mid-x+1) l = mid; else r = mid-1;
}
return l;
}
signed main() {
iofast;
cin >> n; for(int i = 1; i <= n; i++) cin >> a[i];
initST();
vector<PII> segs; //[r,l]
for(int l = 1; l <= n; l++) { //找出所有的bad区间
int r = erfen(l);
if(ask(l,r) == r-l+1) segs.pb({r,l});
}
vector<int> ans(n+1,0);
sort(all(segs));
int now = 0; for(auto &[r,l]: segs)
if(l > now) ++ans[r], now = r; //未被覆盖
for(int i = 1; i <= n; i++) //取前缀和
ans[i] += ans[i-1], cout << ans[i] << ' ';
}

浙公网安备 33010602011771号