bzoj 5028小Z的加油店(D12 序列gcd)(线段树+树状数组)

题意

给定一个序列有两种操作,一种是求一段区间的gcd,另一种是给一段区间加上一个值。(至于为什么看出来是区间gcd,题意应该是在这个区间的瓶子里面倒来倒去,最后要将一个瓶子的油给客户,然后根据裴蜀定理什么的就可以知道了

题解

根据更相减损术可知gcd(x,y)=gcd(x,y-x),那么易得对于三个数gcd(x,y,z)=gcd(x,y-x,z-y);

用数学归纳法容易证明该性质堆任意多个整数都成立

上面的式子有没有很像差分?

所以我们就可以使用差分将区间修改变成两个单点修改,在修改时只要知道a[l]和[l+1,r]这段差分区间的gcd即可。

对于差分数组我们可以建立线段树维护区间gcd,对于单点的值用树状数组维护即可。

注意查询时,如果l==r就直接给出a[l]的值即可,或者在线段树查询时特判返回0,注意取abs

修改时如果r==n就不改r+1了

#include<bits/stdc++.h>
using namespace std;

const int maxn=100005;
int n,m,root,num;
int a[maxn],b[maxn];
int ls[maxn<<1],rs[maxn<<1],gcd[maxn<<1];
int cx[maxn];

template<class T>inline void read(T &x){
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

int get_gcd(int a,int b){
    return !b ? a : get_gcd(b,a%b);
}

void update(int rt){
    gcd[rt]=get_gcd(gcd[ls[rt]],gcd[rs[rt]]);
}

void build(int &rt,int l,int r){
    if(!rt) rt=++num;
    if(l==r) {gcd[rt]=a[l];return ;}
    int mid=(l+r)>>1;
    build(ls[rt],l,mid);
    build(rs[rt],mid+1,r);
    update(rt);
}

void add(int x,int val){for(;x<=n;x+=x&-x) cx[x]+=val;}

int sum(int x){
    int ret=0;
    for(;x;x-=x&-x)
     ret+=cx[x];
    return ret;
}

int query(int rt,int l,int r,int L,int R){
    if(L<=l&&r<=R) return gcd[rt];
    int mid=(l+r)>>1;
    if(R<=mid) return query(ls[rt],l,mid,L,R);
    if(mid<L) return query(rs[rt],mid+1,r,L,R);
    return get_gcd(query(ls[rt],l,mid,L,R),query(rs[rt],mid+1,r,L,R));
}

void modify(int rt,int l,int r,int pos,int val){
    if(l==r){
        gcd[rt]+=val;
        return ;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) modify(ls[rt],l,mid,pos,val);
    else modify(rs[rt],mid+1,r,pos,val);
    update(rt);
}

int main(){
    read(n);read(m);
    for(int i=1;i<=n;i++)read(a[i]);
    for(int i=n;i;i--) a[i]-=a[i-1],add(i,a[i]);
    build(root,1,n);
    for(int i=1;i<=m;i++){
        int op,l,r;
        read(op);read(l);read(r);
        if(l>r) swap(l,r);
        if(op==1){
            if(l==r) printf("%d
",sum(l));
            else printf("%d
",abs(get_gcd(sum(l),query(root,1,n,l+1,r))));
        }
        else {
            int v;
            read(v);
            add(l,v);add(r+1,-v);
            modify(root,1,n,l,v);
            if(r<n) modify(root,1,n,r+1,-v);
        }
    }
}
View Code
原文地址:https://www.cnblogs.com/sto324/p/11240530.html