题意
给定一个序列有两种操作,一种是求一段区间的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); } } }