题目描述
给定一个长度为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
算法提示:
我们知道gcd(x,y)=gcd(x,y-x)。它可以进一步扩展到三个数的情况:gcd(x,y,z)=gcd(x,y-x,z-y)。实际上,给性质对任意多个整数都成立。
因此,我们可以构造一个长度为N的新数列B,其中
B[i]=a[i]-a[i-1],B[1]可为任意值。数列B称为A的差分序列。
用线段树维护序列B的区间最大公约数。
这样一来,询问“Q l r”,就等于求出gcd(A[i],ask(1,l+1,r))。
在指令“C l r d”下,只有B[r+1]被减掉了d,所以在维护B的线段树上只进行两次单点修改即可。另外,询问时需要数列A中的值,可额外用一个支持“区间增加,单点查询”的树状数组对数列A进行维护。
完整代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int SIZE = 500010;
struct SegmentTree {
int l, r;
long long dat;
} t[SIZE * 4];
long long a[SIZE], b[SIZE], c[SIZE];
int n, m;
long long gcd(long long a, long long b) {
return b ? gcd(b, a%b) : a;
}
// 维护区间gcd的线段树
void build(int p, int l, int r) {
t[p].l = l, t[p].r = r;
if (l==r) { t[p].dat = b[l]; return; }
int mid = (l + r) / 2;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
t[p].dat = gcd(t[p*2].dat, t[p*2+1].dat);
}
void change(int p, int x, long long v) {
if (t[p].l == t[p].r) { t[p].dat += v; return; }
int mid = (t[p].l + t[p].r) / 2;
if (x <= mid) change(p*2, x, v);
else change(p*2+1, x, v);
t[p].dat = gcd(t[p*2].dat, t[p*2+1].dat);
}
long long ask(int p, int l, int r) {
if (l <= t[p].l && r >= t[p].r) return abs(t[p].dat);
int mid = (t[p].l + t[p].r) / 2;
long long val = 0;
if (l<=mid) val = gcd(val, ask(p*2, l, r));
if (r>mid) val = gcd(val, ask(p*2+1, l, r));
return abs(val);
}
long long sum(int x) {
long long y = 0;
for (; x; x -= x & -x) y += c[x];
return y;
}
void add(int x, long long y) {
for (; x <= n; x += x & -x) c[x] += y;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
b[i] = a[i] - a[i-1];
}
build(1, 1, n);
for (int i = 1; i <= m; i++) {
char str[2]; scanf("%s", str);
int l, r; scanf("%d%d", &l, &r);
if (str[0] == 'Q') {
long long al = a[l] + sum(l);
long long val = l < r ? ask(1, l + 1, r) : 0;
printf("%lld\n", gcd(al, val));
} else {
long long delta; scanf("%lld", &delta);
change(1, l, delta);
if (r < n) change(1, r + 1, -delta);
add(l, delta);
add(r + 1, -delta);
}
}
}
来源:CSDN
作者:sunday_soft
链接:https://blog.csdn.net/hwdn3000/article/details/104746313