线段树 P3373【区间修改(加法+乘法)区间查询】
题目
思路
参考:https://www.luogu.com.cn/blog/milkfilling/solution-p3373
①加法优先,即规定好segtree[root*2].value=((segtree[root*2].value+segtree[root].add)*segtree[root].mul)%p,问题是这样的话非常不容易进行更新操作,假如改变一下add的数值,mul也要联动变成奇奇怪怪的分数小数损失精度,我们内心是很拒绝的;
②乘法优先,即规定好segtree[root*2].value=(segtree[root*2].value*segtree[root].mul+segtree[root].add*(本区间长度))%p,这样的话假如改变add的数值就只改变add,改变mul的时候把add也对应的乘一下就可以了,没有精度损失,看起来很不错。
先算乘法,再算加法
代码
#include<iostream> #include<algorithm> #include<cstdio> using namespace std; #define MAX 100001 #define INF 0x3f3f3f3f long long a[MAX], c[MAX << 2], lazy[MAX << 2], lazy2[MAX << 2];//空间,一般会开到4*n的空间防止RE。 int n, m,p; void build(long long k, int l, int r) { lazy[k] = 0; lazy2[k] = 1; if (l == r)//如果是最后的叶子节点,就直接赋值 c[k] = a[l]; else { int m = l + ((r - l) >> 1);//注意这里的括号,进行优先级的处理 build(k << 1, l, m);//不是的话就递归 build(k << 1 | 1, m + 1, r); c[k] = c[k << 1] + c[k << 1 | 1];//由于不是最后的叶子节点,所以一定有左右子节点,所以此节点的值就是左右子节点较大的值 } c[k] %= p; } void pushdown(long long k, int l, int r) { //if (lazy[k]) //{ int m = (l + r) >> 1; lazy[k << 1] =( lazy[k << 1]*lazy2[k]+lazy[k])%p; lazy[k << 1 | 1] = (lazy[k << 1 | 1]*lazy2[k]+lazy[k])%p; lazy2[k << 1] = (lazy2[k << 1] * lazy2[k]) % p; lazy2[k << 1|1] = (lazy2[k << 1|1] * lazy2[k]) % p; c[k << 1] = (c[k << 1]*lazy2[k]+ lazy[k] * (m - l + 1))%p; c[k << 1 | 1] = (c[k << 1 | 1]*lazy2[k]+ lazy[k] * (r - m))%p; lazy[k] = 0; lazy2[k] = 1; //} } void update(int L, int R, long long v, int l, int r, int k)//加法 { if (L <= l&&R >= r) { c[k] =(c[k]+ (r - l + 1)* v)%p; lazy[k] = (lazy[k] + v) % p; } else { pushdown(k, l, r); int m = l + ((r - l) >> 1); if (L <= m) update(L, R, v, l, m, k << 1); if (R >m) update(L, R, v, m + 1, r, k << 1 | 1); c[k] = (c[k << 1] + c[k << 1 | 1])%p; } } void update2(int L, int R, long long v, int l, int r, int k) { if (L <= l&&R >= r) { c[k] =c[k]*v%p; lazy[k] = lazy[k] * v%p; lazy2[k] =lazy2[k]*v%p; } else { pushdown(k, l, r); int m = l + ((r - l) >> 1); if (L <= m) update2(L, R, v, l, m, k << 1); if (R >m) update2(L, R, v, m + 1, r, k << 1 | 1); c[k] = (c[k << 1] + c[k << 1 | 1]) % p; } } long long query(int L, int R, int l, int r, long long k) { if (L <= l&&R >= r) return c[k]; else { pushdown(k, l, r); /**每次都需要更新子树的Lazy标记*/ long long res = 0; int m = l + ((r - l) >> 1); if (L <= m)res += query(L, R, l, m, k << 1); if (m < R)res += query(L, R, m + 1, r, k << 1 | 1); return res%p; } } int main() { long long type, x, y, k; scanf("%d %d %d", &n, &m,&p); for (int i = 1; i <= n; i++) scanf("%lld", &a[i]); build(1, 1, n); for (int i = 0; i < m; i++) { scanf("%lld %lld %lld", &type, &x, &y); if (type == 1) { scanf("%lld", &k); update2(x, y, k, 1, n, 1); } else if (type == 2) { scanf("%lld", &k); update(x, y, k, 1, n, 1); } else { printf("%lld\n", query(x, y, 1, n, 1)); } } }