题目地址
题意
定义一个数字序列中如果有一个连续子序列满足这个子序列的最大公约数等于其长度,则称该序列为无聊的。在可以任意修改数字的情况下,对序列 x x x存在函数 f ( x ) f(x) f(x)定义为使得该序列不无聊至少需要修改数字的个数。给定一个序列,求这个序列每个前缀的 f f f函数值。
题解
我们发现可以将数字改为大质数,这样包含这个数字的区间就不可能无聊。
因此假设长度为
i
−
1
i-1
i−1的前缀不无聊,但是长度为
i
i
i的前缀无聊,则仅需修改第
i
i
i个数的贪心显然成立。
再根据最大公约数随序列长度增加而减小的单调规律,在右端点不动的情况下,左端点右移,则
g
c
d
gcd
gcd不降。那么只需要不断右移左端点,直到
g
c
d
≥
区
间
长
度
gcd\geq 区间长度
gcd≥区间长度,然后判断是否相等以及这一段区间是否均未被修改过,若判定成功即修改右端点。这样一来就做完了。
区间
g
c
d
gcd
gcd用st表或者线段树这样的数据结构便可处理,主函数做法用二分或者尺取都可以。
#include<bits/stdc++.h> //Ithea Myse Valgulious
const int yuzu=2e5;
typedef ll fuko[yuzu|10];
fuko a,lg2={-1};
struct sp_table {
fuko gcd[21];
void init(int n) {
for (int i=1;i<=n;++i) gcd[0][i]=a[i];
for (int i=1;i<=19;++i) {
for (int j=1;j+(1<<i)-1<=n;++j)
gcd[i][j]=__gcd(gcd[i-1][j],gcd[i-1][j+(1<<i-1)]);
}
}
ll query(int l,int r) {
int k=lg2[r-l+1];
return __gcd(gcd[k][l],gcd[k][r-(1<<k)+1]);
}
}zw;
int main() {
int n,i,j;
read(n);
for (i=1;i<=n;++i) a[i]=read(),lg2[i]=lg2[i>>1]+1;
zw.init(n);
int ans=0,l=1,ml=-1;
for (i=1;i<=n;++i) {
for (;l<i&&zw.query(l,i)<i-l+1;++l);
if (zw.query(l,i)==i-l+1&&l>ml) {
++ans,ml=i;
}
printf("%d ",ans);
}
}
谢谢大家。