题意:给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:
1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。
2、“Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。
对于每个询问,输出一个整数表示答案。
思路:因为更相减损数gcd(x,y)=gcd(x,y-x),而且正负不影响,所以可以维护A[i]数组的差分的线段树。当进行操作C时,既把A[l]加d,把A[r+1]减d。
然后当进行Q操作时,因为C操作的改变,所以就把[l+1,r]的线段树值求出来,与A[l]进行gcd。求A[l]就可以维护一个树状数组的区间操作,就能够得
到A[i]的值,即可求出答案。注意这题long long。
#include<cstring> #include<algorithm> #include<vector> #include<map> #include<queue> #include<cstdio> #include<cmath> #define ll long long #define lowbit(x) x&(-x) using namespace std; const int N=5e5+100; struct point { ll l; ll r; ll dat; }t[N*4]; ll a[N],c[N],b[N]; ll n,m; void build(int p,ll l,ll r) { t[p].l=l; t[p].r=r; if(l==r) { t[p].dat=c[l]; return; } ll mid=(l+r)>>1; build(p<<1,l,mid); build(p*2+1,mid+1,r); t[p].dat=__gcd(t[p<<1].dat,t[p*2+1].dat); } void update(int p,ll x,ll v) { if(x>n) return; if(t[p].l==t[p].r) { t[p].dat=t[p].dat+v; return; } ll mid=(t[p].l+t[p].r)>>1; if(mid>=x) { update(p<<1,x,v); } else update(p*2+1,x,v); t[p].dat=__gcd(t[p<<1].dat,t[p*2+1].dat); } ll ask(int p,ll l,ll r) { if(l>r) return 0; if(t[p].l>=l&&t[p].r<=r) { return abs(t[p].dat); } ll mid=(t[p].l+t[p].r)>>1; ll vl=0,vr=0; if(mid>=l) { vl=ask(p<<1,l,r); } if(mid<r) { vr=ask(p*2+1,l,r); } return abs(__gcd(vl,vr)); } void add(ll x,ll v) { for(;x<=n;x+=lowbit(x)) { b[x]+=v; } } ll query(ll x) { ll ans=0; for(;x;x-=lowbit(x)) { ans+=b[x]; } return ans; } int main() { scanf("%lld%lld",&n,&m); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); c[i]=a[i]-a[i-1]; } build(1,1,n); char s[5]; ll x,y,z; while(m--) { scanf("%s",s); if(s[0]=='Q') { scanf("%lld%lld",&x,&y); //printf("aa:%lld\n",query(x)+a[x]); // printf("%lld\n",ask(1,x+1,y)); printf("%lld\n",__gcd(a[x]+query(x),ask(1,x+1,y))); } else { scanf("%lld%lld%lld",&x,&y,&z); update(1,x,z); update(1,y+1,-z); add(x,z); add(y+1,-z); } } }