题意
给出一个长度为NNN的序列,每次有MMM条指令,有两种:
“C“C“C lll rrr d”d”d”,表示把Al,Al+1,…,ArA_l,A_l+1,…,A_rAl,Al+1,…,Ar都加上 d。
“Q“Q“Q lll r”r”r”,表示询问Al,Al+1,…,ArA_l,A_l+1,…,A_rAl,Al+1,…,Ar的最大公约数。
思路
根据更相减损法,我们可以知道gcd(x,y)=gcd(x,y−x)gcd(x,y)=gcd(x,y-x)gcd(x,y)=gcd(x,y−x)
同理gcd(x,y,z)=gcd(x,y−z,z−y)gcd(x,y,z)=gcd(x,y-z,z-y)gcd(x,y,z)=gcd(x,y−z,z−y),那么通过这个性质,我们可以用一个差分序列BBB放到线段树里来维护最大公约数,询问的时候就等于求出gcd(Al,ask(1,l+1,r))gcd(A_l,ask(1,l+1,r))gcd(Al,ask(1,l+1,r)),通过差分还可以令它实现区间修改,我们再利用树状数组来维护一开始的序列增加的值。
代码
#include<cstdio>
#include<iostream>
#define ll long long
#define lson(p) ((p) << 1)
#define rson(p) ((p) << 1 | 1)
#define abs(p) ((p) > 0 ? (p) : (-p))
using namespace std;
ll a[500001], c[500001], x, y, z;
struct treenode{
int l, r;
ll v;
}t[2000001];
int n, m;
char check;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
void build(int p, int l, int r) {
t[p].l = l;
t[p].r = r;
if (l == r) {
t[p].v = a[l] - a[l - 1];
return;
}
int mid = (l + r) >> 1;
build(lson(p), l, mid);
build(rson(p), mid + 1, r);
t[p].v = abs(gcd(t[lson(p)].v, t[rson(p)].v));//abs防止差分的gcd是负数
}
ll ask(int p, int l, int r) {
if (t[p].l == l && t[p].r == r) return abs(t[p].v);
int mid = (t[p].l + t[p].r) >> 1;
if (r <= mid) return ask(lson(p), l, r);
else if (l > mid) return ask(rson(p), l, r);
else {
ll a = ask(lson(p), l, mid), b = ask(rson(p), mid + 1, r);
return abs(gcd(a, b));
}
}
ll find(int x) {
ll result = 0;
for (; x; x -= x & -x) result += c[x];
return result;
}
void add(int x, ll v) {
for (; x <= n; x += x & -x) c[x] += v;
}
void change(int p, int x, ll v) {
if (t[p].l == x && t[p].r == x) {
t[p].v += v;
return;
}
int mid = (t[p].l + t[p].r) >> 1;
if (x <= mid) change(lson(p), x, v);
else change(rson(p), x, v);
t[p].v = abs(gcd(t[lson(p)].v, t[rson(p)].v));
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
build(1, 1, n);
while (m--) {
check = getchar();
while (check < 'A' || check > 'Z') check = getchar();
if (check == 'Q') {
scanf("%d %d", &x, &y);
ll ans = gcd(a[x] + find(x), ask(1, x + 1, y));
cout<< ans << endl;
}
else {
scanf("%d %d %lld", &x, &y, &z);
add(x, z);
add(y + 1, -z);//维护区间
change(1, x, z);//维护区间
if (y < n) change(1, y + 1, -z);//防止爆炸
}
}
return 0;
}
本文介绍了一种解决区间加法与最大公约数查询问题的方法。通过使用差分序列和线段树来维护区间更新,并结合树状数组进行区间内元素的增减操作。最后利用更相减损法原理,巧妙地实现了最大公约数的快速查询。
358

被折叠的 条评论
为什么被折叠?



