D. Interval_GCD
Blue book title Interval GCD
title meaning: Given n numbers Ai, M operations (N: 5e5, M: 5e5) have two operations:
- C lrd adds d to A[l,r]
- Q lr asks the greatest common divisor of A[l,r]
思路:
gcd ( a 1 , a 2 , a 3 , a 4 . . . , a n ) = gcd ( a 1 , a 2 − a 1 , a 3 − a 2 , a 4 − a 3 . . . a n − a n − 1 ) \gcd(a_1,a_2,a_3,a_4...,a_n)=\gcd(a_1,a_2-a_1,a_3-a_2,a_4-a_3...a_n-a_{n-1}) gcd(a1,a2,a3,a4...,an)=gcd(a1,a2−a1,a3−a2,a4−a3...an−an−1)
所以: gcd (al, al + 1, al + 2... Ar) = gcd (al, al + 1 - al, al + 2 - al + 1... Ar - ar - 1) \ gcd ( a_l, a_ {l + 1}, a_ {l + 2} ... a_ {r}) = gcd (a_l, a_ {l + 1} -a_ {l}, a_ {l + 2} -a_ {l +1} ... a_ {r} -a_ {r-1})gcd(al,al+1,al+2...ar)=gcd(al,al+1−al,al+2−al+1...ar−ar−1)
The sequence maintained in this way is converted from the original sequence A to the difference sequence B of A, Now all that remains is for al a_lalThe
advantage of maintenance is:
for each operation C (interval addition), only a single point of modification of the ll of the B sequencel andr + 1 r+1r+1 position (llAdd d to l ;r + 1 r+1r+1 minus d)
Method : Establish a line segment tree to maintain differential sequence B (in order to calculate the gcd of differential sequence B \gcdg cd )
Establish the grooming number maintenance series A (in order to calculateal a_lal)
struct SegmentTree{
int l,r;
LL dat;//计算gcd
#define l(x) tree[x].l
#define r(x) tree[x].r
#define dat(x) tree[x].dat
}tree[maxn*4];
LL a[maxn],b[maxn],c[maxn];
int n,m;
void build(int p,int l,int r){
l(p)=l,r(p)=r;
if(l==r){
dat(p)=b[l];return;}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
dat(p)=lgcd(dat(p*2),dat(p*2+1));
}
/*void lazy(int p){
if(add(p)){
sum(p*2)+=add(p)*(r(p*2)-l(p*2)+1);
sum(p*2+1)+=add(p)*(r(p*2+1)-l(p*2+1)+1);
add(p*2)+=add(p);
add(p*2+1)+=add(p);
add(p)=0;
}
}*/
void change(int p,int x,LL z){
if(l(p)==r(p)){
dat(p)+=z;return;}
int mid=(l(p)+r(p))/2;
if(x<=mid) change(p*2,x,z);
else change(p*2+1,x,z);
dat(p)=lgcd(dat(p*2),dat(p*2+1));
}
LL ask(int p,int l,int r){
if(l<=l(p)&&r>=r(p)) return abs(dat(p));
int mid=(l(p)+r(p))/2;
LL ans=0ll;
if(l<=mid)ans=lgcd(ans,ask(p*2,l,r));
if(r>mid)ans=lgcd(ans,ask(2*p+1,l,r));
return abs(ans);
}
void updata(int i,LL k){
//在i位置上加上k
while(i<=n){
c[i]+=k;
i+=i&(-i);
}
}///区间[p,q]加c:updata(p,c);updata(q+1,-c);
LL getsum(int i){
//求i的前缀和(1~i)
LL res=0;
while(i){
res+=c[i];
i-=i&(-i);
}
return res;
}///区间[p,q]:getsum(q)-getsum(p-1)
int main(){
cin>>n>>m;
memset(c,0,sizeof c);
for(int i=1;i<=n;i++){
cin>>a[i];b[i]=a[i]-a[i-1];}
build(1,1,n);
int x,y;
LL z;
char op;
while(m--){
cin>>op>>x>>y;
if(op=='C'){
cin>>z;
updata(x,z);//维护数组A
change(1,x,z);//维护数组B
if(y+1<=n){
change(1,y+1,-1ll*z);updata(y+1,-1l*z);}
}else{
cout<<lgcd(a[x]+getsum(x),ask(1,x+1,y))<<endl;}
}
}