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;
}
posted @ 2021-05-18 02:07  acmloser  阅读(59)  评论(0编辑  收藏  举报