AcWing 246. 区间最大公约数
原题链接
考察:线段树
思路:
第一个操作:表示把 A[l],A[l+1],…,A[r] 都加上 d.显而易见的差分优化为单点修改.
第二个操作:求[L,R]区间的gcd.
通过这两个操作思考怎么定义结构体.模板L,R然后很明显需要一个变量val记录[L,R]区间内的gcd.但是如果我们把线段树维护的数组定义为差分数组,那么求gcd(A[l],A[l+1],A[l+2],...,A[r])就比较难求.
这里要想到辗转相除术:gcd(a,b) = gcd(b,a-b);
扩展到多个.gcd(A[l],A[l+1],A[l+2],...,A[R]) 与 gcd(A[l],A[l+1]-A[l],A[l+2]-A[l],...A[r]-A[r-1])是否相等.
大致的证明思路是先证>=然后<=.关于证明>=,假设右边的gcd能被左边整除即可.同理<=
想到这个就比较简单了.要求的gcd就是gcd(A[l],A[l+1]-A[l],A[l+2]-A[l+1],...,A[r]-A[r-1]).注意除了第一个A[l],剩下的可以用线段树维护gcd,A[l]需要差分数组求前缀和.这里可以用树状数组,也可以再让线段树维护一个值
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 500010;
struct Node{
int l,r;
LL val,sum;
}tr[N<<2];
int n,m;
char s[2];
LL a[N],b[N];
LL gcd(LL a,LL b)
{
return b?gcd(b,a%b):a;
}
void push_up(int u)
{
tr[u].sum = tr[u<<1].sum+tr[u<<1|1].sum;
tr[u].val = gcd(tr[u<<1].val,tr[u<<1|1].val);
}
void build(int u,int l,int r)
{
tr[u].l = l,tr[u].r = r;
if(l==r) { tr[u].val = a[l];tr[u].sum = a[l] ;return; }
int mid = l+r>>1;
build(u<<1,l,mid); build(u<<1|1,mid+1,r);
push_up(u);
}
void modify(int u,int idx,LL x)
{
if(tr[u].l==tr[u].r) {tr[u].val += x;tr[u].sum+=x ;return; }
int mid = tr[u].l+tr[u].r>>1;
if(idx<=mid) modify(u<<1,idx,x);
else modify(u<<1|1,idx,x);
push_up(u);
}
LL query_sum(int u,int l,int r)
{
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;
LL res = 0;
int mid = tr[u].l+tr[u].r>>1;
if(mid>=l) res+=query_sum(u<<1,l,r);
if(mid<r) res+=query_sum(u<<1|1,l,r);
return res;
}
LL query(int u,int l,int r)//询问[l,r]区间的最大gcd
{
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].val;
int mid = tr[u].l+tr[u].r>>1;
LL res = 0;
if(l<=mid) res = gcd(query(u<<1,l,r),res);
if(mid<r) res =gcd(query(u<<1|1,l,r),res);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&b[i]),a[i] = b[i]-b[i-1];
build(1,1,n);
while(m--)
{
int l,r; LL d;
scanf("%s%d%d",s,&l,&r);
if(s[0]=='C')
{
scanf("%lld",&d);
modify(1,l,d);
if(r+1<=n) modify(1,r+1,-d);
}else{
LL sum = query_sum(1,1,l),g = 0;
if(l+1<=r) g = query(1,l+1,r);
printf("%lld\n",abs(gcd(sum,g)));
}
}
return 0;
}