CHOJ 4302 Interval GCD

描述

给定一个长度为N的数列A,以及M条指令 (N≤5*10^5, M<=10^5),每条指令可能是以下两种之一:
“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。
“Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。
输入格式

第一行两个整数N,M,第二行N个整数Ai,接下来M行每条指令的格式如题目描述所示。
输出格式

对于每个询问,输出一个整数表示答案。
样例输入

5 5
1 3 5 7 9
Q 1 5
C 1 5 1
Q 1 5
C 3 3 6
Q 2 4

样例输出

1
2
4

数据范围与约定

N,M≤2*10^5,l<=r,数据保证任何时刻序列中的数都是不超过2^62-1的正整数。

根据更相减损法,gcd(x,y)=gcd(x,y-x),当然,这条式子拓展到n个元素仍然成立,所以这题我们也利用了这个条件,然后我们也可以把这一题给做出来了。
首先我们观察这个式子,就是一个差分式子,所以就用线段树维护区间gcd,线段树维护区间gcd就是在回溯的时候,把原来的pushup弄成搞gcd的就行。所以gcd(y-x)以后的答案都可以弄出来,根据差分的性质,区间加可以变成单点加,当然gcd的首个元素x我们可以用树状数组维护,因为我们一直在差分数组上操作嘛。当然维护差分序列的时候要记得判边界,然后因为对树状数组理解不够……导致卡了2h……记得维护树状数组要在另一个数组上进行,不能在原差分数列上进行。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
ll a[N],b[N],c[N];
int n,m;
ll mabs(ll x)
{
	return x>=0?x:-x;
}
ll gcd(ll x,ll y)
{
	if(y==0)
	return x;
	else
	return gcd(y,x%y);
}
ll lowbit(ll x)
{
	return x&(-x);
}
ll asks(ll x)
{
	ll ans=0;
	while(x>0)
	{
		ans+=c[x];
		x-=lowbit(x);
	}
	return ans;
}
void adds(ll x,ll zhi)
{
	while(x<=n)
	{
		c[x]+=zhi;
		x+=lowbit(x);
	}
}
struct node
{
	ll l,r,g;
}t[N*4];
void build(ll rt,ll l,ll r)
{
    t[rt].l=l;
	t[rt].r=r;
	if(l==r)
	{
		t[rt].g=b[l];
		return;
	}
	ll mid=(l+r)>>1;
	build(rt*2,l,mid);
	build(rt*2+1,mid+1,r);
	t[rt].g=gcd(t[rt*2].g,t[rt*2+1].g);
}
void upd(ll rt,ll x,ll zhi)
{
	if(t[rt].l==x&&t[rt].r==x)
	{
		t[rt].g+=zhi;
		return;
	}
	ll mid=(t[rt].l+t[rt].r)>>1;
	if(x<=mid)
	{
		upd(rt*2,x,zhi);
	}
	else
	{
		upd(rt*2+1,x,zhi);
	}
	t[rt].g=gcd(t[rt*2].g,t[rt*2+1].g);
}
ll askx(ll rt,ll l,ll r)
{
	if(l<=t[rt].l&&r>=t[rt].r)
	{
		return t[rt].g;
	}
	ll mid=(t[rt].l+t[rt].r)>>1;
	if(r<=mid)
	{
		return askx(rt*2,l,r);
	}
	if(l>mid)
	{
		return askx(rt*2+1,l,r);
	}
	ll val;
	return val=gcd(askx(rt*2,l,r),askx(rt*2+1,l,r));
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=n;i++)
	{
		b[i]=a[i]-a[i-1];
		adds(i,b[i]);
	}
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		char c;
		cin>>c;
		if(c=='Q')
		{
			ll l,r;
			scanf("%lld %lld",&l,&r);
			if(l<r)//判边界,如果l==r,就直接用树状数组输出第一个元素 
			printf("%lld\n",gcd(asks(l),mabs(askx(1,l+1,r))));//这里要加个取绝对值,根据c++的标准,%一个负数的结果是根据被模数决定的,而差分出现负数是肥肠正常的,所以需要abs 
			else
			{
				printf("%lld\n",asks(l));
			}
		}
		else
		{
			ll l,r,d;
			scanf("%lld %lld %lld",&l,&r,&d);
			adds(l,d);
			upd(1,l,d);
			if(r+1<=n)//判边界,如果r+1,即减的部分超出范围,就不用管了 
			{
				adds(r+1,-d);
				upd(1,r+1,-d);
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_37073764/article/details/82952628
gcd