描述
给定一个长度为N的数列A,以及M条指令 (N≤510^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≤210^5, l<=r,数据保证任何时刻序列中的数都是不超过2^62-1的正整数。
不要在意数据不统一的事
区间 g c d gcd gcd好做
难点在于如何维护区间加
考虑辗转相减法:
g
c
d
(
x
,
y
)
=
g
c
d
(
x
,
x
−
y
)
gcd(x,y)=gcd(x,x-y)
gcd(x,y)=gcd(x,x−y)
我们可以将这个东西拓展到三个数:
g
c
d
(
x
,
y
,
z
)
=
g
c
d
(
x
,
y
−
x
,
z
−
y
)
gcd(x,y,z)=gcd(x,y-x,z-y)
gcd(x,y,z)=gcd(x,y−x,z−y)
证明(摘自 g s j gsj gsj神仙的 b l o g blog blog):
设 g c d ( x , y , z ) = a gcd(x,y,z)=a gcd(x,y,z)=a,那么必然存在互质的三个数 k 1 , k 2 , k 3 k_1,k_2,k_3 k1,k2,k3使 x = k 1 a , y = k 2 a , z = k 3 a x=k_1a,y=k_2a,z=k_3a x=k1a,y=k2a,z=k3a
那么 g c d ( x , y − x , z − y ) = g c d ( k 1 a , ( k 2 − k 1 ) a , ( k 3 − k 2 ) a ) gcd(x,y-x,z-y)=gcd(k_1a,(k_2-k_1)a,(k_3-k_2)a) gcd(x,y−x,z−y)=gcd(k1a,(k2−k1)a,(k3−k2)a)
由于 k 1 , k 2 , k 3 k_1,k_2,k_3 k1,k2,k3互质,则 k 1 , k 2 − k 1 , k 3 − k 2 k_1,k_2-k_1,k_3-k_2 k1,k2−k1,k3−k2都必然互质
那么我们要求的区间
g
c
d
gcd
gcd就可以变成差分数组
g
c
d
(
a
[
l
]
,
d
e
l
[
l
+
1
]
gcd(a[l],del[l+1]
gcd(a[l],del[l+1],…,
d
e
l
[
r
]
)
del[r])
del[r])
我们发现这时候区间加其实只对应了
l
,
r
+
1
l,r+1
l,r+1的单点加减
那我们可以先线段树维护区间差分数组的 g c d gcd gcd
再单独维护一个单点值
最后合起来计算一下 g c d gcd gcd就可以了
复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res*f;
}
const int N=5000005;
int tr[N<<2],bit[N],a[N],n,m;
inline int lowbit(int x){
return (x&(-x));
}
inline void update(int pos,int k){
for(;pos<=n;pos+=lowbit(pos))bit[pos]+=k;
}
inline int bitsum(int pos,int res=0){
for(;pos;pos-=lowbit(pos))res+=bit[pos];return res;
}
int gcd(int x,int y){
return y==0?x:gcd(y,x%y);
}
#define lc (u<<1)
#define rc ((u<<1)|1)
#define mid ((l+r)>>1)
inline void pushup(int u){
tr[u]=gcd(tr[lc],tr[rc]);
}
void buildtree(int u,int l,int r){
if(l==r){
tr[u]=a[l]-a[l-1];return;
}
buildtree(lc,l,mid);
buildtree(rc,mid+1,r);
pushup(u);
}
void add(int u,int l,int r,int pos,int k){
if(l==r){
tr[u]+=k;return;
}
if(mid<pos)add(rc,mid+1,r,pos,k);
else add(lc,l,mid,pos,k);
pushup(u);
}
int query(int u,int l,int r,int st,int des){
if(st<=l&&r<=des){
return tr[u];
}
if(des<=mid)return query(lc,l,mid,st,des);
if(mid<st)return query(rc,mid+1,r,st,des);
return gcd(query(lc,l,mid,st,des),query(rc,mid+1,r,st,des));
}
signed main(){
n=read();m=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
buildtree(1,1,n);
for(int i=1;i<=m;i++){
char op[4];
scanf("%s",op);
switch(op[0]){
case 'Q':{
int l=read(),r=read();
cout<<abs(gcd(bitsum(l)+a[l],query(1,1,n,l+1,r)))<<'\n';
break;
}
case 'C':{
int l=read(),r=read(),v=read();
add(1,1,n,l,v);if(r<n)add(1,1,n,r+1,-v);
update(l,v);if(r<n)update(r+1,-v);
break;
}
}
}
}