链接:https://www.acwing.com/problem/content/description/247/
246. 区间最大公约数
神仙题。
区间加数,求区间gcd。n,q<=500000
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=500005;
struct node{
int l,r;
LL sum,d;
}tr[N*4];
int n,m,x,y;
LL a[N],z;
char op;
LL gcd(LL x,LL y){
if(y==0)return x;
return gcd(y,x%y);
}
void pushup(node &u,node &l,node &r){
u.sum=l.sum+r.sum;
u.d=gcd(l.d,r.d);
}
void pushup(int w){
pushup(tr[w],tr[w<<1],tr[w<<1|1]);
}
void build(int w,int l,int r){
//cout<<"init:"<<w<<" "<<l<<" "<<r<<endl;
if(l==r){
tr[w]={l,r,a[l]-a[l-1],a[l]-a[l-1]};
}else{
tr[w]={l,r};
int mid=(l+r)>>1;
build(w<<1,l,mid);
build(w<<1|1,mid+1,r);
pushup(w);
}
}
node query(int w,int l,int r){
//cout<<w<<" "<<tr[w].l<<" "<<tr[w].r<<endl;
if(l<=tr[w].l&&r>=tr[w].r)return tr[w];
int mid=(tr[w].l+tr[w].r)>>1;
if(r<=mid)return query(w<<1,l,r);
else if(l>mid)return query(w<<1|1,l,r);
else{
node res,left,right;
left=query(w<<1,l,r);
right=query(w<<1|1,l,r);
pushup(res,left,right);
return res;
}
}
void modify(int w,int x,LL y){
if(tr[w].l==tr[w].r){
tr[w].sum+=y;
tr[w].d+=y;
}else{
int mid=(tr[w].l+tr[w].r)>>1;
if(x<=mid)modify(w<<1,x,y);
else modify(w<<1|1,x,y);
pushup(w);
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
while(m--){
cin>>op;
if(op=='Q'){
cin>>x>>y;
LL ans=query(1,1,x).sum;
if(x+1<=y)ans=gcd(ans,query(1,x+1,y).d);
cout<<abs(ans)<<endl;
}else{
cin>>x>>y>>z;
modify(1,x,z);
if(y<n)modify(1,y+1,-z);
}
}
return 0;
}
用差分将区间gcd求解转化成差分求解,线段树维护差分的前缀和与gcd。
gcd(a,b,c,…)=gcd(a,b-a,c-b,…)
上面这个等式可以用充分必要性来证,通过左边证右边,通过右边证左边,两问题等价。
故将问题转化成差分数组后用线段树维护差分数组的前缀和与差分数组的gcd。