Files
python/TangDou/AcWing/SegmentTree/246.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

111 lines
3.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
// 线段树,单点修改,区间查询
#define int long long
#define ls (u << 1)
#define rs (u << 1 | 1)
#define mid ((l + r) >> 1)
int n, m;
int a[N];
struct Node {
int l, r;
int sum; // 区间总和
int d; // 区间内的最大公约数
} tr[N << 2];
// 求最大公约数
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
void pushup(int u) {
tr[u].sum = tr[ls].sum + tr[rs].sum; // 更新父节点的区间和
tr[u].d = gcd(tr[ls].d, tr[rs].d); // 计算区间的最大公约数
}
// 构建
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
int b = a[r] - a[r - 1]; // 更相减损数,所以按原数组差分构建,yxc大佬很良心修改了试题,添加了1e18的数据范围说明
tr[u].sum = tr[u].d = b; // 当是叶子节点时,区间和就是自己,区间最大公约数也是自己
return;
}
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
// 以u为根的子树中,修改位置为x的节点,值为+v
void change(int u, int x, int v) {
int l = tr[u].l, r = tr[u].r;
if (l == r) { // 叶子
tr[u].sum += v; // 叶子值+d
tr[u].d = tr[u].sum; // 叶子,就是一个数,不是区间,最大公约数是自身
return;
}
if (x <= mid)
change(u << 1, x, v);
else
change(u << 1 | 1, x, v);
pushup(u);
}
// 查询
Node query(int u, int L, int R) {
int l = tr[u].l, r = tr[u].r;
if (l > R || r < L) return {0, 0, 0, 0};
// 如果与当前区间无交集,则返回一个空对象,注意根据题意修改空对象的初始值
// 任何数与0的最大公约数都是该数本身
if (l >= L && r <= R) return tr[u]; // 完全命中,返回命中区间
// 左右两部分区间
Node a = query(ls, L, R); // 不要惧怕区间划分问题,因为上面特判了无交集情况
Node b = query(rs, L, R);
// 合并区间结果,即使有一半区间无交集,也能正确返回数据,因为无交集返回的是空对象,而空对象对于后续的运算是无影响的
Node res;
res.sum = a.sum + b.sum; // 更新父节点的区间和
res.d = gcd(a.d, b.d); // 计算区间的最大公约数
return res;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("246.in", "r", stdin);
#endif
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
// 因为差分的r+1可能越界,这里在建立线段树时就多创建一个位置就OK!
build(1, 1, n + 1);
int l, r;
int d;
char op;
while (m--) {
cin >> op >> l >> r;
if (op == 'Q') {
Node lc = query(1, 1, l); // 1~l求sum
// 如果存在后半段
if (l < r) {
Node rc = query(1, l + 1, r);
printf("%lld\n", abs(gcd(lc.sum, rc.d)));
} else // l==r
// 如果不存在后半段,那么就只有前半部分的sum和
printf("%lld\n", abs(lc.sum));
} else {
cin >> d;
// 差分
change(1, l, d), change(1, r + 1, -d); // 就是这里用到的r+1,为了避免特判,所以相当于给线段树加了一个右哨兵
}
}
return 0;
}