描述
给定一个长度为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);
}
}
}
}