Link
ST表维护gcd, 2000
题意
给出 a a a 数组,若 g c d ( a l , a l + 1 , . . . a r ) = r − l + 1 gcd(a_l, a_{l+1},...a_r) = r - l + 1 gcd(al,al+1,...ar)=r−l+1,则称区间 [ l , r ] [l,r] [l,r]是boring 的。设 f ( x ) f(x) f(x)表示使这个数列的前x位不存在boring区间所需的最小修改次数(每次可以修改区间某个位置的值为任意值)。 依次输出 f ( 1 ) 到 f ( n ) f(1)到f(n) f(1)到f(n)
思路
显然,如果要修改某个数的话,可以将它修改为一个很大的质数,这样一定不会对后面产生影响,相当于隔断了两边,分成了不同的段。此外,我们要尽可能最小化修改的次数。也就是让修改的值尽量靠后。具体来说,可以用左指针 l l l 表示当前段的最左端,从1到n枚举右指针 r r r,若当前段gcd小于区间长度,r右移,否则若gcd==长度则ans++,l=r+1,否则l++。
代码
int n, k;
int a[maxn][22];
int gcd(int a, int b) {
if(b == 0) return a;
return gcd(b, a % b);
}
int gd(int l, int r) {
if(l > r) return INF;
int k = 0;
while((1 <<(k+1)) <= r-l+1) k++; //如果2^(k+1) <= r-l+1 k++
return gcd(a[l][k], a[r -(1<<k)+1][k]);
}
void solve() {
cin >> n;
for(int i =1; i <= n; i++) cin >> a[i][0];
for(int j = 1; (1<<j)<= n; j++) {
for(int i = 1; i+(1<<j)-1<=n; i++) {
a[i][j] = gcd(a[i][j-1], a[i+(1<<(j-1))][j-1]);
}
}
int ans = 0, l = 1;
for(int r = 1; r <= n; r++) {
while(gd(l, r) <= (r - l + 1)) {
if(gd(l, r) == (r - l + 1)) {
l = r + 1;
ans++;
}
else
l++;
}
cout << ans << ' ';
}
}