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