题面要求两个操作
- 区间加
- 求区间的最大公约数
结论
g c d ( a 1 , a 2 , . . . , a n ) = g c d ( a 1 , b 2 , . . . , b n ) gcd(a_1,a_2,...,a_n) = gcd(a_1,b_2,...,b_n) gcd(a1,a2,...,an)=gcd(a1,b2,...,bn)
b 1 = a 1 , b i = a i − a i − 1 , i ≥ 2 b_1=a_1,b_i=a_i-a_{i-1},i≥2 b1=a1,bi=ai−ai−1,i≥2
b b b是 a a a的差分数组
思路
因为要求区间的一个最大公约数;
如果涉及区间加的话,我们不好维护;
如果能转化为单点修改,那么这个公约数可以从最底层迭代的求上来;
因此我们考虑差分;
假设
b
1
=
a
1
,
b
i
=
a
i
−
a
i
−
1
,
i
≥
2
b_1=a_1,b_i=a_i-a_{i-1},i≥2
b1=a1,bi=ai−ai−1,i≥2
g
c
d
(
a
1
,
a
2
,
.
.
.
,
a
n
)
=
g
c
d
(
a
1
,
b
2
,
.
.
.
,
b
n
)
gcd(a_1,a_2,...,a_n) = gcd(a_1,b_2,...,b_n)
gcd(a1,a2,...,an)=gcd(a1,b2,...,bn)
证明
先证
g
c
d
(
a
1
,
a
2
,
.
.
.
,
a
n
)
≤
g
c
d
(
a
1
,
b
2
,
.
.
.
,
b
n
)
gcd(a_1,a_2,...,a_n) ≤ gcd(a_1,b_2,...,b_n)
gcd(a1,a2,...,an)≤gcd(a1,b2,...,bn)
因为右边是由左边做差(线性运算)得到的,因此左边的gcd必然是右边的一个公约数,不一定最大,因此右边必然≥左边;
再证
g
c
d
(
a
1
,
a
2
,
.
.
.
,
a
n
)
≥
g
c
d
(
a
1
,
b
2
,
.
.
.
,
b
n
)
gcd(a_1,a_2,...,a_n) ≥ gcd(a_1,b_2,...,b_n)
gcd(a1,a2,...,an)≥gcd(a1,b2,...,bn)
显然右边的gcd可以整除 a 1 a_1 a1,那么看看 a 2 a_2 a2
a 2 = b 2 + a 1 a_2=b_2+a_1 a2=b2+a1,也就是左边都可以由右边线性运算得到;
因此右边的gcd必然是左边的一个公约数,不一定最大,因此左边必然≥右边;
因此
g
c
d
(
a
1
,
a
2
,
.
.
.
,
a
n
)
=
g
c
d
(
a
1
,
b
2
,
.
.
.
,
b
n
)
gcd(a_1,a_2,...,a_n) = gcd(a_1,b_2,...,b_n)
gcd(a1,a2,...,an)=gcd(a1,b2,...,bn)
那么要求 [ L , R ] [L,R] [L,R]的 g c d gcd gcd的话,我们需要用到上面的式子;
因此求 g c d ( a L , g c d ( [ b L + 1 , b R ] ) ) gcd(a_L,gcd([b_{L+1},b_R])) gcd(aL,gcd([bL+1,bR]))即可
a L a_L aL也就是差分数组求和;
Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
typedef long long LL;
const int N = 500010;
int n, m;
LL w[N];
struct Node
{
int l, r;
LL sum, d;//d是gcd
}tr[N<<2];
LL gcd(LL a, LL b)
{
return b ? gcd(b, a % b) : a;
}
Node node_merge(Node left,Node right){
Node ret;
ret.l = left.l,ret.r = right.r;
ret.sum = left.sum + right.sum;
ret.d = gcd(left.d,right.d);
return ret;
}
void push_up(int p){
tr[p] = node_merge(tr[lc],tr[rc]);
}
void build(int p, int l, int r)
{
tr[p].l = l,tr[p].r = r;
if (l == r)
{
LL b = w[r] - w[r - 1];
tr[p].sum = tr[p].d = b;
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid), build(rc, mid + 1, r);
push_up(p);
}
Node query(int p,int l,int r){
if(tr[p].l>=l&&tr[p].r<=r){
return tr[p];
}
int mid = (tr[p].l+tr[p].r) >> 1;
if(r<=mid){
return query(lc,l,r);
}else if(l > mid) return query(rc,l,r);
else{
return node_merge(
query(lc,l,mid),query(rc,mid+1,r)
);
}
}
void node_update(int p,int x,LL u){
if(tr[p].l == tr[p].r){
LL tmp = tr[p].sum + u;
tr[p].sum = tr[p].d = tmp;
return;
}
int mid = (tr[p].l + tr[p].r) >> 1;
if(x>mid){
node_update(rc,x,u);
}else{
node_update(lc,x,u);
}
push_up(p);
}
LL _abs(LL x){
return x>=0?x:-x;
}
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,m;
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> w[i];
build(1, 1, n);
int l, r;
LL d;
char op;
while (m -- )
{
cin >> op >> l >> r;
if (op == 'Q')
{
auto left = query(1, 1, l);
Node right;
if (l + 1 <= r) right = query(1, l + 1, r);
cout << _abs(gcd(left.sum, right.d)) << '\n';
}
else
{
cin >> d;
node_update(1, l, d);
if (r + 1 <= n) node_update(1, r + 1, -d);
}
}
return 0;
}