【题目】
题目描述:
给定一个长度为 n n n 的数列 a a a,以及 m m m 条指令 ( n ≤ 5 × 1 0 5 , m ≤ 1 0 5 ) (n≤5\times10^5, m\le10^5) (n≤5×105,m≤105),每条指令可能是以下两种之一:
“
C
  
l
  
r
  
d
”
“C\; l\; r\; d”
“Clrd”,表示把
a
l
,
a
l
+
1
,
…
,
a
r
a_l,a_{l+1},…,a_r
al,al+1,…,ar 都加上
d
d
d。
“
Q
  
l
  
r
”
“Q\; l \;r”
“Qlr”,表示询问
a
l
,
a
l
+
1
,
…
,
a
r
a_l,a_{l+1},…,a_r
al,al+1,…,ar 的最大公约数
(
g
c
d
)
(gcd)
(gcd)。
输入格式:
第一行两个整数 n , m n,m n,m,第二行 n n n 个整数 a i a_i ai,接下来 m m 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 × 1 0 5 n,m≤2\times10^5 n,m≤2×105, l ≤ r l\le r l≤r,数据保证任何时刻序列中的数都是不超过 2 62 − 1 2^{62}-1 262−1的正整数。
【分析】
数据范围为什么都不统一啊。。。
做这道题之前,要先知道一个东西,叫做辗转相减法
有一个关于它的推论,即 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),推广到 n n n 个数也成立
这里还是简单证一下(如果有错请提出来)
假设 g c d ( x , y , z ) = a gcd(x,y,z)=a gcd(x,y,z)=a,那么必有互质的三个数 m 1 , m 2 , m 3 m_1,m_2,m_3 m1,m2,m3 使 x = m 1 a , y = m 2 a , z = m 3 a x=m_1a,y=m_2a,z=m_3a x=m1a,y=m2a,z=m3a
那么 g c d ( x , y − x , z − y ) = g c d ( m 1 a , ( m 2 − m 1 ) a , ( m 3 − m 2 ) a ) gcd(x,y-x,z-y)=gcd(m_1a,(m_2-m_1)a,(m_3-m_2)a) gcd(x,y−x,z−y)=gcd(m1a,(m2−m1)a,(m3−m2)a)
由于 m 1 , m 2 , m 3 m_1,m_2,m_3 m1,m2,m3 互质,所以 m 1 , m 2 − m 1 , m 3 − m 2 m_1,m_2-m_1,m_3-m_2 m1,m2−m1,m3−m2 必然也互质,所以 g c d ( x , y − x , z − y ) = a = g c d ( x , y , z ) gcd(x,y-x,z-y)=a=gcd(x,y,z) gcd(x,y−x,z−y)=a=gcd(x,y,z)
那么,知道了这个之后又有什么用呢
我们可以用差分的思想,把原数组变为 a 1 , a 2 − a 1 , a 3 − a 2 , … , a n − a n − 1 a_1,a_2-a_1,a_3-a_2,\dots ,a_n-a_{n-1} a1,a2−a1,a3−a2,…,an−an−1(用 s u m i sum_i sumi 表示 a i − a i − 1 a_i-a_{i-1} ai−ai−1)
这样的话,修改就好办了,直接在 s u m l sum_l suml 加 d d d,在 s u m r + 1 sum_{r+1} sumr+1 减 d d d(中间的数加的 d d d 相互抵消了)
查询的时候,根据辗转相减法,先找出 g c d ( s u m l + 1 , s u m l + 2 , … , s u m r ) gcd(sum_{l+1},sum_{l+2},\dots ,sum_{r}) gcd(suml+1,suml+2,…,sumr),由于此时 s u m l sum_{l} suml 记录的是 a l − a l − 1 a_{l}-a_{l-1} al−al−1,要单独处理一下,而不难看出 s u m 1 + s u m 2 + ⋯ + s u m l = a l sum_1+sum_2+\dots +sum_l=a_l sum1+sum2+⋯+suml=al,所以答案为
a n s = g c d ( ∑ i = 1 l s u m i , g c d ( s u m l + 1 , s u m l + 2 , … , s u m r ) ) ans=gcd(\sum^{l}_{i=1}sum_i,gcd(sum_{l+1},sum_{l+2},\dots,sum_r)) ans=gcd(i=1∑lsumi,gcd(suml+1,suml+2,…,sumr))
因此,用一个树状数组记录 s u m sum sum 的前缀和,用一个线段树记录 s u m sum sum 的区间 g c d gcd gcd 就可以了
【代码】
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 500005
#define ll long long
#define lowbit(x) x&-x
using namespace std;
int n,m;
ll a[N],bit[N],Gcd[N<<2];
void add(int i,ll x)
{
while(i<=n)
{
bit[i]+=x;
i+=lowbit(i);
}
}
ll sum(int i)
{
ll ans=0;
while(i)
{
ans+=bit[i];
i-=lowbit(i);
}
return ans;
}
void build(int root,int l,int r)
{
if(l==r)
{
Gcd[root]=a[l]-a[l-1];
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
Gcd[root]=__gcd(Gcd[root<<1],Gcd[root<<1|1]);
}
void modify(int root,int l,int r,int x,ll k)
{
if(l==r)
{
Gcd[root]+=k;
return;
}
int mid=(l+r)>>1;
if(x<=mid) modify(root<<1,l,mid,x,k);
else modify(root<<1|1,mid+1,r,x,k);
Gcd[root]=__gcd(Gcd[root<<1],Gcd[root<<1|1]);
}
ll query(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return Gcd[root];
int mid=(l+r)>>1;
if(y<=mid) return query(root<<1,l,mid,x,y);
if(x>mid) return query(root<<1|1,mid+1,r,x,y);
return __gcd(query(root<<1,l,mid,x,y),query(root<<1|1,mid+1,r,x,y));
}
int main()
{
int x,y,i; ll k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
scanf("%lld",&a[i]);
build(1,1,n);
for(i=1;i<=m;++i)
{
char c; cin>>c;
scanf("%d%d",&x,&y);
if(c=='C')
{
scanf("%lld",&k);
add(x,k),modify(1,1,n,x,k);
if(y<n)add(y+1,-k),modify(1,1,n,y+1,-k);
}
else printf("%lld\n",abs(__gcd(a[x]+sum(x),query(1,1,n,x+1,y))));
}
return 0;
}