【CH 4302】Interval GCD

本文介绍了如何利用线段树和树状数组解决区间最大公约数(GCD)查询和更新的问题。通过差分存储数组,并在每个节点维护区间的GCD,可以高效地进行区间操作。同时,提到了在64位整数绝对值计算中可能遇到的陷阱,需要注意避免越界问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给定一个长度为 N N ​ 的数列 A A ​ ,以及 M M ​ 条指令 ( N5×105,M<=105 N ≤ 5 × 10 5 , M <= 10 5 ​ ),每条指令可能是以下两种之一:

“C l r d”,表示把 A[l],A[l+1],,A[r] A [ l ] , A [ l + 1 ] , … , A [ r ] 都加上 d d

“Q l r”,表示询问 A[l],A[l+1],,A[r] 的最大公约数(GCD)。

算法分析

根据 gcd(x,y,z)=gcd(x,yx,zy) g c d ( x , y , z ) = g c d ( x , y − x , z − y ) ,将原数组差分存入线段树,每个节点储存该节点所代表区间的最大公约数,再开一个树状数组单独维护每个节点的值,则查询答案就是 gcd(BIT::query(l),SGT::query(l+1,r)) g c d ( B I T :: q u e r y ( l ) , S G T :: q u e r y ( l + 1 , r ) ) ,注意要判断操作线段树和树状数组是否越界。

另外,cstdlib 中计算 64 64 位整数绝对值的函数是 llabs,使用 abs 会返回 Wrong Anwer,尽管本地评测可能是对的……

代码实现

#include <cstdio>
#include <cstring>
#include <cstdlib>
typedef long long int ll;
const int maxn=(int)5e5+5;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
namespace BIT {
    int n;ll bit[maxn];
    inline void init(int N) {
        n=N;memset(bit,0,sizeof(bit));
    }
    inline void add(int x,ll d) {
        for(;x<=n;x+=x&-x) bit[x]+=d;
    }
    inline ll query(int x) {
        ll ans=0;
        for(;x;x-=x&-x) ans+=bit[x];
        return ans;
    }
}
namespace SGT {
    int n;ll ans[4*maxn];
    void build(int o,int l,int r,ll arr[]) {
        int mid=(l+r)>>1;
        if(l==r) ans[o]=arr[mid];
        else {
            build(o<<1,l,mid,arr);
            build(o<<1|1,mid+1,r,arr);
            ans[o]=gcd(ans[o<<1],ans[o<<1|1]);
        }
    }
    inline void init(int N,ll arr[]) {n=N;build(1,1,n,arr);}
    int q,ql,qr;
    void add(int o,int l,int r,ll d) {
        int mid=(l+r)>>1;
        if(l==r) ans[o]+=d;
        else {
            if(q<=mid) add(o<<1,l,mid,d);
            else if(mid+1<=q) add(o<<1|1,mid+1,r,d);
            ans[o]=gcd(ans[o<<1],ans[o<<1|1]);
        }
    }
    inline void add(int x,ll d) {q=x;add(1,1,n,d);}
    ll query(int o,int l,int r) {
        int mid=(l+r)>>1;
        if(ql<=l&&r<=qr) return ans[o];
        else {
            if(ql<=mid&&mid+1<=qr) return gcd(query(o<<1,l,mid),query(o<<1|1,mid+1,r));
            else if(ql<=mid) return query(o<<1,l,mid);
            else if(mid+1<=qr) return query(o<<1|1,mid+1,r);
        }
        return 0;
    }
    inline ll query(int l,int r) {ql=l;qr=r;return query(1,1,n);}
}
ll a[maxn];
int main() {
    int n,m;scanf("%d%d",&n,&m);
    ll last=0,x;BIT::init(n);
    for(int i=1;i<=n;++i) {
        scanf("%lld",&x);
        BIT::add(i,x-last);
        a[i]=x-last;last=x;
    }
    char s[2];int l,r;ll d;SGT::init(n,a);
    while(m--) {
        scanf("%s%d%d",s,&l,&r);
        if(s[0]=='C') {
            scanf("%lld",&d);
            BIT::add(l,d);SGT::add(l,d);
            if(r+1<=n) {BIT::add(r+1,-d);SGT::add(r+1,-d);}
        }
        else if(s[0]=='Q') {
            if(l+1<=r) printf("%lld\n",gcd(BIT::query(l),llabs(SGT::query(l+1,r))));
            else printf("%lld\n",BIT::query(l));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值