长度为n(n<=5e5)的数组a[],m(m<=2e5)次操作,操作分两种
①对[l,r]区间加d ②询问[l,r]区间的gcd
题目保证ai在任何操作之后都为不超过的正整数
https://blog.csdn.net/forever_dreams/article/details/85222870
注意到gcd(x,y,z)==gcd(x,y-x,z-y),推广到n个数也成立
证明的话,就设它们的gcd为k,然后用x,y,z分别去除以k,
所得数是三个互质的数,那么它们作差也互质,
有了这个性质之后,就可以直接维护差分数组,
化区间修改为单点修改,a[l]加上d,a[r+1]减去d
询问gcd(l,l+1,...,r)=gcd(a[l],a[l+1]-a[l],...,a[r]-a[r-1]),
这样,a[l]用BIT维护差分数组前缀和求,后面的用线段树维护差分数组gcd
trick:差分值可能是负的,好在gcd只是多了负号,取绝对值即可
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
int n,m,l,r;
char op[5];
//tr[]:维护差分数组前缀和
//gcd[]:维护差分数组区间gcd
ll res,v,a[maxn],tr[maxn],gcd[maxn*5];
void add(int x,ll v)
{
for(int i=x;i<=n;i+=i&-i)
tr[i]+=v;
}
ll sum(int x)
{
ll ans=0;
for(int i=x;i>0;i-=i&-i)
ans+=tr[i];
return ans;
}
void pushup(int p)
{
gcd[p]=__gcd(gcd[p<<1],gcd[p<<1|1]);
}
void build(int p,int l,int r)
{
if(l==r)
{
gcd[p]=a[l];
return;
}
int m=l+r>>1;
build(p<<1,l,m);
build(p<<1|1,m+1,r);
pushup(p);
}
void update(int p,int l,int r,int pos,ll v)
{
if(l==r)
{
gcd[p]+=v;
return;
}
int m=l+r>>1;
if(pos<=m)update(p<<1,l,m,pos,v);
else update(p<<1|1,m+1,r,pos,v);
pushup(p);
}
ll ask(int p,int l,int r,int ql,int qr)
{
if(ql>qr)return 0;
if(ql<=l&&r<=qr)return gcd[p];
ll res=0;
int m=l+r>>1;
if(ql<=m)res=__gcd(res,ask(p<<1,l,m,ql,qr));
if(qr>m)res=__gcd(res,ask(p<<1|1,m+1,r,ql,qr));
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]);
for(int i=n;i>=1;--i)
{
a[i]=a[i]-a[i-1];
add(i,a[i]);
}
build(1,1,n);
while(m--)
{
scanf("%s",op);
if(op[0]=='C')
{
scanf("%d%d%lld",&l,&r,&v);
add(l,v);
update(1,1,n,l,v);
if(r+1<=n)
{
add(r+1,-v);
update(1,1,n,r+1,-v);
}
}
else if(op[0]=='Q')
{
scanf("%d%d",&l,&r);
printf("%lld\n",abs(__gcd(sum(l),ask(1,1,n,l+1,r))));
}
}
return 0;
}