CF1632D. New Year Concert

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)=rl+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 << ' ';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值