传送门 NC 26255
只考虑后两种操作很容易用线段树维护,但第一种操作需要进行区间修改,复杂度可能退化为 O ( n ) O(n) O(n)。考虑差分处理。差分的好处在于将区间更新变为单点更新,求单点原值的时候 O ( l o g n ) O(logn) O(logn) 求前缀和即可。问题在于如何维护区间的最大公约数。
根据更相减损术(类似于减法版的辗转相除法),最大公约数有如下性质 g c d ( x , y , z … ) = g c d ( x , y − x , z − y , … ) gcd(x,y,z\dots)=gcd(x,y-x,z-y,\dots) gcd(x,y,z…)=gcd(x,y−x,z−y,…) 容易证明,当差值为负时取绝对值依然满足上式。设 d [ i ] = c o l [ i ] − c o l [ i − 1 ] d[i]=col[i]-col[i-1] d[i]=col[i]−col[i−1],则区间的最大公约数为 g c d ( ∑ i = 1 l d [ i ] , d [ l + 1 ] , … , d [ r ] ) gcd(\sum\limits_{i=1}^{l}d[i],d[l+1],\dots,d[r]) gcd(i=1∑ld[i],d[l+1],…,d[r]) 那么线段树维护差分数组的区间和,区间绝对值的最大值与区间的最大公约数即可。
#include <bits/stdc++.h> using namespace std; #define maxn 100005 #define sg_size (1 << 18) - 1 struct node { int gcd, mx, sum; } sg[sg_size]; int n, m, col[maxn]; int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } inline void pushup(int k, int chl, int chr) { sg[k].gcd = gcd(sg[chl].gcd, sg[chr].gcd); sg[k].mx = max(sg[chl].mx, sg[chr].mx); sg[k].sum = sg[chl].sum + sg[chr].sum; } void init(int k, int l, int r) { if (r - l == 1) { int d = col[l] - col[l - 1]; sg[k] = node{abs(d), abs(d), d}; } else { int chl = (k << 1) + 1, chr = (k << 1) + 2, m = (l + r) >> 1; init(chl, l, m); init(chr, m, r); pushup(k, chl, chr); } } void update(int a, int b, int x, int k, int l, int r) { if (a <= l && r <= b) { int d = sg[k].sum + x; sg[k] = node{abs(d), abs(d), d}; } else if (l < b && a < r) { int chl = (k << 1) + 1, chr = (k << 1) + 2, m = (l + r) >> 1; update(a, b, x, chl, l, m); update(a, b, x, chr, m, r); pushup(k, chl, chr); } } node query(int a, int b, int k, int l, int r) { if (b <= l || r <= a) { return node{0, 0, 0}; } else if (a <= l && r <= b) { return sg[k]; } int chl = (k << 1) + 1, chr = (k << 1) + 2, m = (l + r) >> 1; node c = query(a, b, chl, l, m); node d = query(a, b, chr, m, r); return node{gcd(c.gcd, d.gcd), max(c.mx, d.mx), c.sum + d.sum}; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", col + i); } int ub = n + 1; init(0, 1, ub); while (m--) { int op, l, r; scanf("%d%d%d", &op, &l, &r); if (op == 1) { int x; scanf("%d", &x); update(l, l + 1, x, 0, 1, ub); update(r + 1, r + 2, -x, 0, 1, ub); } else if (op == 2) { printf("%d\n", query(l + 1, r + 1, 0, 1, ub).mx); } else { int a = query(1, l + 1, 0, 1, ub).sum; int b = query(l + 1, r + 1, 0, 1, ub).gcd; printf("%d\n", gcd(a, b)); } } return 0; }