New Year Concert

New Year Concert

Created by LXC on Mon Jul 17 00:22:47 2023

https://codeforces.com/problemset/problem/1632/D

ranting: 2000

tag: binary search, data structures, greedy, math, number theory, two pointers

problem

给出n个数,我们需要查询每个前缀需要修改最少多少个数,使得不存在子数组的最大公约数等于子数组长度。

$n \le 2\cdot10^5$

solution

一个性质,区间越大,区间的gcd就越小。所以在已知所有区间gcd的情况下,对于固定左端点l的区间我们可以通过二分法找到区间右端点r,区间[l,r]为不合法区间(满足gcd等于区间长度r-l+1)。

我们可以将r设置为巨大的质数,这时所有包含了r的不合法区间都会变合法。然后继续从r+1开始二分寻找不合法区间。我们寻找的不合法区间不会重叠。

我们此外需要快速得知区间的gcd,暴力$O(n^2)$,妥妥超时。可以用st表预处理,预处理$O(nlogn)$,每次查询$O(logn)$

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

#include <bits/stdc++.h>
#define SINGLE_INPUT
#define ll long long
#define ull unsigned long long
#define N 500005
#define MOD 998244353
using namespace std;

#define MAXN 200005
int st[MAXN][20]; // st[i][j] 代表区间[i, i+2^j)最小值

void ST(const vector<int>& a) {
int n = a.size();
for (int i = 0; i < n; i++)
st[i][0] = a[i];
for (int j = 1; (1 << j) <= n; j++) { // 区间大小
for (int i = 0; i + (1 << j) - 1 < n; i++) { // 区间下限
st[i][j] = __gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
}
}

int ask(int l, int r) {
int k = 0;
while ((1 << (k + 1)) <= r - l + 1)
k++;
return __gcd(st[l][k], st[r - (1 << k) + 1][k]);
}

void sol() {
int n;
cin >> n;
vector<int> a(n);
for (auto& i : a)
cin >> i;
ST(a);
int p = 0;
vector<int> ans(n, 0);
while (p < n) {
int l = p, r = n;
while (l < r) {
int m = l + r >> 1;
if (ask(p, m) > m - p + 1) {
l = m + 1;
} else {
r = m;
}
}
if (r != n && ask(p, r) == r - p + 1) {
ans[r] = 1;
p = r + 1;
} else {
p++;
}
}
for (int i = 0, pre = 0; i < n; i++) {
pre += ans[i];
cout << pre << " ";
}
cout << "\n";
}

int main() {
cout << setprecision(15) << fixed;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifndef SINGLE_INPUT
int t;
cin >> t;
while (t--) {
sol();
}
#else
sol();
#endif
return 0;
}