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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}