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