
通过gcd的性质和差分,将区间修改简化为点修。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ls u<<1
#define rs u<<1|1
const int N=500010;
typedef long long LL;
int n,m; LL a[N],b[N];
struct Tree{ //线段树
int l,r;
LL sum,d; //差分序列的区间和,最大公约数
}tr[N*4];
LL gcd(LL a,LL b){
return b ? gcd(b,a%b) : a;
}
void pushup(Tree &u,Tree l,Tree r){
u.sum=l.sum+r.sum;
u.d=gcd(l.d,r.d);
}
void build(int u,int l,int r){ //建树
tr[u]={l,r,b[l],b[l]};
if(l==r) return;
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(tr[u],tr[ls],tr[rs]);
}
void change(int u,int x,LL v){ //点修
if(tr[u].l==tr[u].r){
tr[u].sum+=v; tr[u].d+=v;
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid) change(ls,x,v);
else change(rs,x,v);
pushup(tr[u],tr[ls],tr[rs]);
}
Tree query(int u,int l,int r){ //区查
if(l<=tr[u].l && tr[u].r<=r) return tr[u];
int mid=tr[u].l+tr[u].r>>1;
//先判断是否完全被包裹,之后再在下面的push去走左右儿子区间。
if(r<=mid) return query(ls,l,r);
if(l>mid) return query(rs,l,r);
Tree T; //开一个临时节点,存储拼凑结果
pushup(T,query(ls,l,r),query(rs,l,r));
return T;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]), b[i]=a[i]-a[i-1];
build(1,1,n);
while(m --){
char c[1]; int l,r; scanf("%s%d%d",c,&l,&r);
if(*c=='C'){
LL v; scanf("%lld",&v);
change(1,l,v);
if(r+1<=n) change(1,r+1,-v); //r=n时,越界
}
else{
Tree L, R={0,0,0,0};
L=query(1,1,l); //a[l]=sum(b[1~l])
//第一次查得到L,是为了得到a[l],即sum,第二次是得到R.d
if(l+1<=r) R=query(1,l+1,r);//b[l+1~r]的gcd
printf("%lld\n",abs(gcd(L.sum, R.d)));
}
}
return 0;
}