You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

111 lines
3.4 KiB

2 years ago
#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;
}