[CF438D]The Child and Sequence
题目大意:给定一个数列,三个操作:
- $1\;l\;r:$输出区间$[l,r]$的和
- $2\;l\;r\;x:$区间$[l,r]$对$x$取模
- $3\;p\;x:$把$p$的值修改成$x$
题解:线段树,发现对于取模操作,若模数大于被膜数,则不会有影响,可以通过维护最大值跳过,若是对有影响,那么这个数字一定至少会变成原来的一半,所以一个数最多更改$\log_2$次,可以承受。
卡点:我$\max$函数写成$\min$
C++ Code:
#include <cstdio> #define maxn 100010 int s[maxn]; inline int max(int a, int b) {return a > b ? a : b;} namespace Segment_Tree { int n; long long V[maxn << 2]; int M[maxn << 2]; inline update(int rt) { M[rt] = max(M[rt << 1], M[rt << 1 | 1]); V[rt] = V[rt << 1] + V[rt << 1 | 1]; } void build(int rt = 1, int l = 1, int r = n) { if (l == r) { M[rt] = V[rt] = s[l]; return ; } int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); update(rt); } int L, R, p, x; void ___add(int rt, int l, int r) { if (l == r) { V[rt] = M[rt] = x; return ; } int mid = l + r >> 1; if (p <= mid) ___add(rt << 1, l, mid); else ___add(rt << 1 | 1, mid + 1, r); update(rt); } inline void add(int pos, int X) { p = pos, x = X; ___add(1, 1, n); } void __add(int rt, int l, int r) { if (M[rt] < x) return ; if (l == r) { V[rt] = M[rt] = V[rt] % x; return ; } int mid = l + r >> 1; if (L <= mid) __add(rt << 1, l, mid); if (R > mid) __add(rt << 1 | 1, mid + 1, r); update(rt); } inline void add(int ll, int rr, int X) { L = ll, R = rr, x = X; __add(1, 1, n); } long long __ask(int rt, int l, int r) { if (L <= l && R >= r) return V[rt]; int mid = l + r >> 1; long long ans = 0; if (L <= mid) ans = __ask(rt << 1, l, mid); if (R > mid) ans = ans + __ask(rt << 1 | 1, mid + 1, r); return ans; } inline long long ask(int ll, int rr) { L = ll, R = rr; return __ask(1, 1, n); } } using Segment_Tree::add; using Segment_Tree::ask; int n, m; int main() { scanf("%d%d", &n, &m); Segment_Tree::n = n; for (int i = 1; i <= n; i++) scanf("%d", s + i); Segment_Tree::build(); while (m --> 0) { int op, l, r, val; scanf("%d%d%d", &op, &l, &r); if (op == 1) { printf("%lld\n", ask(l, r)); } else if (op == 2) { scanf("%d", &val); add(l, r, val); } else add(l, r); } return 0; }