题目链接
246. 区间最大公约数 - AcWing题库
思路:
①首先要知道由更相减损法我们可得,gcd(a,b,c,d,...)=gcd(a,b-a,c-b,d-c,...)
那么我们便可用线段树维护差分序列的gcd,查询区间[l,r]的gcd时就等于gcd(a[l],query(l+1,r))。
对于区间[l,r]加上k,对于差分序列而言,相当于d[l]+k,而d[r+1]-k,直接线段树单点修改就可以。
用树状数组维护区间[l,r]加k后每个数相对于原来的偏移,查询时a[l]实际等于a[l]+ask(l)。
②在数值加减的过程中可能会产生负数,而约定gcd是没有负数的,根据gcd(a,b)=gcd(a,−b),在每次查询或者更新的时候,如果遇到了负数结果,要将它取反。注意只能对结果取反而不能直接把线段树的负数叶子节点取反,直接把叶子取反会对今后的加减操作造成影响,gcd(a+1,b)!=gcd(-a+1,b)。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef struct Node{
int l,r;
ll _gcd;
}Node;
const int N=5e5+10;
Node tr[N*4];
ll trs[N];
ll a[N],d[N];
int n,m;
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int lowbit(int x){
return x
}
void update(int x,ll d){
while(x<=n){
trs[x]+=d;
x+=lowbit(x);
}
}
ll ask(int x){
ll res=0;
while(x){
res+=trs[x];
x-=lowbit(x);
}
return res;
}
void pushup(int u){
tr[u]._gcd=gcd(tr[u<<1]._gcd,tr[u<<1|1]._gcd);
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r){
tr[u]._gcd=d[l];
return ;
}
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int pos,ll k){
if(tr[u].l==tr[u].r){
tr[u]._gcd+=k;
return ;
}
int mid=(tr[u].l+tr[u].r)>>1;
if(pos<=mid)
modify(u<<1,pos,k);
else
modify(u<<1|1,pos,k);
pushup(u);
}
ll query(int u,int l,int r){
if(r<l)
return 0;
if(tr[u].l>=l&&tr[u].r<=r)
return tr[u]._gcd;
ll res=0;
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid)
res=gcd(res,query(u<<1,l,r));
if(r>mid)
res=gcd(res,query(u<<1|1,l,r));
return abs(res);
}
int main(){
scanf("%d%d",
for(int i=1;i<=n;i++){
scanf("%lld",
}
for(int i=1;i<=n;i++){
d[i]=a[i]-a[i-1];
}
build(1,1,n);
char op[2];
int l,r;
ll k,res1,res2;
while(m--){
scanf("%s",op);
if(*op=='Q'){
scanf("%d%d",
res1=a[l]+ask(l);
res2=query(1,l+1,r);
printf("%lld\n",abs(gcd(res1,res2)));
}
else{
scanf("%d%d%lld",
modify(1,l,k);
if(r<n)
modify(1,r+1,-k);
update(l,k);
update(r+1,-k);
}
}
}