P10463 Interval GCD
P10463 Interval GCD
思路
首先,有个性质:对于任意多整数,它们的最大公约数与它们的差分序列的最大公约数相同,可以通过以下证明。
\(\forall a, b, c \in \mathbb{N} \text{,有} \gcd(a, b, c) = \gcd(a, b - a, c - b)\)
\(\text{证明:设} d \mid a, d \mid b, d \mid c\)
\(a = k_1 d, b = k_2 d, c = k_3 d, (k_1, k_2, k_3 \in \mathbb{N})\)
\(\because b - a = (k_2 - k_1)d, c - b = (k_3 - k_2)d\)
\(\therefore d \mid {b - a}, d \mid {c - b}\)
易发现,把 \(3\) 个数拓展到任意多整数,该性质都是成立的。
所以可以构造 \(A\) 的差分序列 \(B\),用线段树来维护 \(B\) 的区间最大公约数,询问区间最大公约数就可以通过求 \(A_l\) 与 \(B\) 中 \(l + 1\) 到 \(r\) 区间的最大公约数来解决。
这样,在执行操作 1 时,就只用让 \(B_l + d\) , \(B_{r + 1} - d\) ,不会改变中间的数,也就不用把区间中每个数都再取出来求最大公约数了!
此外因为询问需要用到数列 \(A\) 中的值,可以用树状数组来维护 \(A\) 中的数值。
有一点需要注意,差分序列的可能有负数,所以询问时要取绝对值
代码
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 5e5 + 5;
#define ll long long
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define mid ((l + r) >> 1)
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
int n, m;
ll a[N], tr[N << 2], b[N], c[N];
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a;}
void up(int rt) { tr[rt] = gcd(tr[ls], tr[rs]); }
void build(int rt, int l, int r) {
if (l == r) { tr[rt] = b[l]; return; }
build(ls, l, mid), build(rs, mid + 1, r);
up(rt);
}
void update(int rt, int l, int r, int x, ll v) {
if (l == r){ tr[rt] += v; return; }
if (x <= mid) update(ls, l, mid, x, v);
else update(rs, mid + 1, r, x, v);
up(rt);
}
ll query(int rt, int l, int r, int L, int R) {
if (L <= l && r <= R) return abs(tr[rt]);
ll val = 0;
if (mid >= L) val = gcd(val, query(ls, l, mid, L, R));
if (mid < R) val = gcd(val, query(rs, mid + 1, r, L, R));
return abs(val);
}
ll sum(int x) {
ll y = 0;
for (; x; x -= x & -x) y += c[x];
return y;
}
void add(int x, ll y) { for (; x <= n; x += x & -x) c[x] += y; }
int main()
{
scanf("%d%d", &n, &m);
LF(i, 1, n) {
scanf("%lld", &a[i]);
b[i] = a[i] - a[i - 1];
}
build(1, 1, n);
LF(i, 1, m) {
int l, r;
char s[2];
scanf("%s%d%d", s, &l, &r);
if (s[0] == 'Q') {
ll al = a[l] + sum(l);
ll val = l < r ? query(1, 1, n, l + 1, r) : 0;
printf("%lld\n", gcd(al, val));
}
else {
ll d;
scanf("%lld", &d);
update(1, 1, n, l, d);
if (r < n) update(1, 1, n, r + 1, -d);
add(l, d);
add(r + 1, -d);
}
}
return 0;
}