《算法竞赛进阶指南》0x43线段树 区间最大公约数

题目链接:https://www.acwing.com/problem/content/247/

操作有两种:求区间最大公约数+区间修改。

两者的结合可以运用更相损减术,维护一个差分数列,用一个树状数组维护数列的差分前缀和,其中要注意的是,树状数组和线段树的维护中要防止索引的越界!!其次,gcd得到的数可能是负数,所以在进入最小性的区间中之后需要对gcd取绝对值,gcd(a,-b)=gcd(a,b)。不能直接对差分之后的区间的gcd取绝对值,因为gcd(a+1,b)!=gcd(-a+1,b)。要在答案返回的时候进行更新。

代码:

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = 500010;
ll a[maxn],b[maxn],c[maxn];
int n,m;
ll gcd(ll x,ll y){
    return y?gcd(y,x%y):x;
}
struct node{
    int l,r;
    ll gcd;
}t[maxn<<2]; 
ll sum(int x){//差分数列的前缀和 
    ll ans=0;
    while(x){
        ans+=c[x];
        x-=x&(-x);
    }
    return ans;
}
ll add(int x,ll C){//树状数组单点更新 
    while(x<=n){
        c[x]+=C;
        x+=x&(-x);
    }
}
void build(int rt,int l,int r){
    t[rt].l=l;
    t[rt].r=r;
    if(l==r){
        t[rt].gcd=b[l];
        return ;
    }
    int mid=(t[rt].l+t[rt].r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    t[rt].gcd=gcd(t[rt<<1].gcd,t[rt<<1|1].gcd);
}
void update(int rt,int pos,ll C){
    if(t[rt].l==t[rt].r){
        t[rt].gcd+=C;
        return ;
    }
    int mid=(t[rt].l+t[rt].r)>>1;
    if(pos<=mid)update(rt<<1,pos,C);
    else update(rt<<1|1,pos,C);
    t[rt].gcd=gcd(t[rt<<1].gcd,t[rt<<1|1].gcd);
}
ll query(int rt,int L,int R){
    if(t[rt].l>=L && t[rt].r<=R){
        return abs(t[rt].gcd);
    }
    int mid=(t[rt].l+t[rt].r)>>1;
    ll ans=0;
    if(L<=mid)ans=gcd(ans,query(rt<<1,L,R));
    if(R>mid)ans=gcd(ans,query(rt<<1|1,L,R));
    return ans;
}
int main(){
    cin>>n>>m;
    b[0]=0;
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        b[i]=a[i]-a[i-1];
    }
    build(1,1,n);
    char s[10];
    int l,r;
    ll d;
    while(m--){
        scanf("%s",s);
        if(s[0]=='C'){
            scanf("%d%d%lld",&l,&r,&d);
            update(1,l,d);//维护两个差分数列 
            add(l,d);
            if(r+1<=n){//注意防止树状数组的下标越界 
                update(1,r+1,-d);
                add(r+1,-d);    
            }
        }else{
            scanf("%d%d",&l,&r);
            ll ans=gcd(a[l]+sum(l),query(1,l+1,r));
            printf("%lld\n",ans);
        }
    }
}

 

posted @ 2020-07-14 10:18  WA自动机~  阅读(196)  评论(0编辑  收藏  举报