P3373 【模板】线段树 2
题目链接
既有区间乘又有区间加,那么乘和加的顺序很关键
- 先乘后加: a ∗ b + c a*b+c a∗b+c ,自然没什么影响
- 先加后乘: ( a + b ) ∗ c = a ∗ c + b ∗ c (a+b)*c=a*c+b*c (a+b)∗c=a∗c+b∗c ,如果我们不进行处理,仅仅单独做乘和加的标记,就会变得混乱
我们只需要对加法标记乘上 c c c 就行,也就是把这个式子的括号展开都变成加。
code
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdio>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 100000 + 10, M = 100000 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
ll n, m, p;
struct Tree {
ll sum, add, mul;
} tree[N << 2];
#define ls(rt) (rt << 1)
#define rs(rt) (rt << 1 | 1)
void push_up(ll root) { tree[root].sum = (tree[ls(root)].sum + tree[rs(root)].sum) % p; }
void push_down(ll root, ll len) {
// 规定先乘后加,如果有先加后乘,就把这个式子展开,变成先乘后加,比如(a+b)*c=a*c+b*c
// add已经乘过mul了,所以直接加在sum上就行
tree[ls(root)].sum = (tree[ls(root)].sum * tree[root].mul + tree[root].add * (len - (len >> 1))) % p;
tree[rs(root)].sum = (tree[rs(root)].sum * tree[root].mul + tree[root].add * (len >> 1)) % p;
tree[ls(root)].mul = tree[ls(root)].mul * tree[root].mul % p;
tree[rs(root)].mul = tree[rs(root)].mul * tree[root].mul % p;
tree[ls(root)].add = (tree[ls(root)].add * tree[root].mul + tree[root].add) % p;
tree[rs(root)].add = (tree[rs(root)].add * tree[root].mul + tree[root].add) % p;
tree[root].mul = 1, tree[root].add = 0;
}
void build_tree(ll l, ll r, ll root) {
tree[root].mul = 1;
if (l == r) {
ll x;
scanf("%lld", &x);
tree[root].sum = x % p;
return;
}
ll mid = (l + r) >> 1;
build_tree(l, mid, ls(root));
build_tree(mid + 1, r, rs(root));
push_up(root);
}
void mul(ll L, ll R, ll val, ll l, ll r, ll root) {
if (L <= l && r <= R) {
tree[root].add = (tree[root].add * val) % p;
tree[root].mul = (tree[root].mul * val) % p;
tree[root].sum = (tree[root].sum * val) % p;
return;
}
push_down(root, r - l + 1);
ll mid = (l + r) >> 1;
if (L <= mid) mul(L, R, val, l, mid, ls(root));
if (mid < R) mul(L, R, val, mid + 1, r, rs(root));
push_up(root);
}
void add(ll L, ll R, ll val, ll l, ll r, ll root) {
if (L <= l && r <= R) {
tree[root].add = (tree[root].add + val) % p;
tree[root].sum = (tree[root].sum + val * (r - l + 1)) % p;
return;
}
push_down(root, r - l + 1);
ll mid = (l + r) >> 1;
if (L <= mid) add(L, R, val, l, mid, ls(root));
if (mid < R) add(L, R, val, mid + 1, r, rs(root));
push_up(root);
}
ll query(ll L, ll R, ll l, ll r, ll root) {
if (L <= l && r <= R) {
return tree[root].sum;
}
push_down(root, r - l + 1);
ll res = 0;
ll mid = (l + r) >> 1;
if (L <= mid) res = (res + query(L, R, l, mid, ls(root))) % p;
if (mid < R) res = (res + query(L, R, mid + 1, r, rs(root))) % p;
return res;
}
int main() {
scanf("%lld%lld%lld", &n, &m, &p);
build_tree(1, n, 1);
while (m--) {
ll t, x, y, k;
scanf("%lld", &t);
if (t == 1) {
scanf("%lld%lld%lld", &x, &y, &k);
mul(x, y, k, 1, n, 1);
} else if (t == 2) {
scanf("%lld%lld%lld", &x, &y, &k);
add(x, y, k, 1, n, 1);
} else {
scanf("%lld%lld", &x, &y);
printf("%lld\n", query(x, y, 1, n, 1));
}
}
return 0;
}