New Year Concert
[Link](Problem - D - Codeforces)
题意
给你一个序列你可以将更改其中的数变成任意一个数,问你最少更改几个数使得该序列的任意一个子区间的 g c d gcd gcd都不等于该区间长度。
思路
首先我们从左到右来看这个序列,按照以每个位置结尾将所有的子区间划分,发现更改其实就是将这些区间弄了个墙,比如枚举到了第 i i i个位置那么假设上次更改的位置是 x x x这次我们只需要找 x + 1 , i x+1,i x+1,i这个区间内的以 i i i结尾的区间是否存在不满足的即可,因为如果在 x x x前面到 i i i有个不合法的区间我们通过更改 x x x的值将这个区间变得合法,直觉上来想一定是区间越长 g c d gcd gcd越小,所以靠率二分 x + 1 , i x+1,i x+1,i是否存在等于长度的 g c d gcd gcd如果存在就直接在 i i i更改即可,查询区间 g c d gcd gcd可以离线 s t st st与处理一下。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int f[N][20];
int find(int l, int r) {
if (l > r) return 0;
int j = log2(r - l + 1);
return __gcd(f[l][j], f[r - (1 << j) + 1][j]);
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> f[i][0];
for (int j = 1; j <= 19; j ++)
for (int i = 1; i + (1 << j) - 1 <= n; i ++)
f[i][j] = __gcd(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
int res = 0;
for (int i = 1, j = 0; i <= n; i ++) {
int l = j + 1, r = i;
while (l < r) {
int mid = l + r >> 1;
if (find(mid, i) >= i - mid + 1) r = mid;
else l = mid + 1;
}
if (find(l, i) == i - l + 1)
res ++, j = i;
cout << res << ' ';
}
return 0;
}
再进一步思考发现,其实还有区间 g c d gcd gcd还有一些性质,我们以 i i i为起点的区间使得区间长度等于 g c d gcd gcd的区间最多只有一个,假设 g c d ( i , . . . , j ) = j − i + 1 gcd(i,...,j)=j-i+1 gcd(i,...,j)=j−i+1那么再往外扩展一定不会使 g c d gcd gcd变大,而往左减少长度不会使 g c d gcd gcd变小,因此以某个点为起点和以每个点为终点的 g c d gcd gcd最多只有一个,因此我们可以先从左往右二分预处理出来每个唯一的区间,然后对于这些区间做一下类似于区间合并算贡献。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int f[N][20];
int g[N];
int find(int l, int r) {
if (l > r) return 0;
int j = log2(r - l + 1);
return __gcd(f[l][j], f[r - (1 << j) + 1][j]);
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> f[i][0];
for (int j = 1; j <= 19; j ++)
for (int i = 1; i + (1 << j) - 1 <= n; i ++)
f[i][j] = __gcd(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
for (int i = 1; i <= n; i ++) {
int l = i, r = n;
while (l < r) {
int mid = l + r >> 1;
if (find(i, mid) <= mid - i + 1) r = mid;
else l = mid + 1;
}
if (find(i, l) == l - i + 1) g[l] = i;
}
int res = 0;
for (int i = 1, j = 0; i <= n; i ++) {
if (j < g[i]) res ++, j = i;
cout << res << ' ';
}
return 0;
}