C++P3373 线段树 2 — 完整代码(乘加双标记)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
ll MOD;
ll sum[MAXN * 4], add[MAXN * 4], mul[MAXN * 4], a[MAXN];
int n, m;
#define lc (p << 1)
#define rc (p << 1 | 1)
#define mid ((l + r) >> 1)
void pushup(int p) { sum[p] = (sum[lc] + sum[rc]) % MOD; }
void build(int p, int l, int r) {
mul[p] = 1; add[p] = 0;
if (l == r) { sum[p] = a[l] % MOD; return; }
build(lc, l, mid); build(rc, mid + 1, r);
pushup(p);
}
void pushdown(int p, int l, int r) {
if (mul[p] == 1 && add[p] == 0) return; // 无标记
// 左孩子
sum[lc] = (sum[lc] * mul[p] + add[p] * (mid - l + 1)) % MOD;
mul[lc] = mul[lc] * mul[p] % MOD;
add[lc] = (add[lc] * mul[p] + add[p]) % MOD;
// 右孩子
sum[rc] = (sum[rc] * mul[p] + add[p] * (r - mid)) % MOD;
mul[rc] = mul[rc] * mul[p] % MOD;
add[rc] = (add[rc] * mul[p] + add[p]) % MOD;
// 清空
mul[p] = 1; add[p] = 0;
}
void rangeMul(int p, int l, int r, int L, int R, ll k) {
if (L <= l && r <= R) {
sum[p] = sum[p] * k % MOD;
mul[p] = mul[p] * k % MOD;
add[p] = add[p] * k % MOD; // ★ 加法标记也要乘
return;
}
pushdown(p, l, r);
if (L <= mid) rangeMul(lc, l, mid, L, R, k);
if (R > mid) rangeMul(rc, mid + 1, r, L, R, k);
pushup(p);
}
void rangeAdd(int p, int l, int r, int L, int R, ll v) {
if (L <= l && r <= R) {
sum[p] = (sum[p] + v * (r - l + 1)) % MOD;
add[p] = (add[p] + v) % MOD;
return;
}
pushdown(p, l, r);
if (L <= mid) rangeAdd(lc, l, mid, L, R, v);
if (R > mid) rangeAdd(rc, mid + 1, r, L, R, v);
pushup(p);
}
ll query(int p, int l, int r, int L, int R) {
if (L <= l && r <= R) return sum[p];
pushdown(p, l, r);
ll res = 0;
if (L <= mid) res = (res + query(lc, l, mid, L, R)) % MOD;
if (R > mid) res = (res + query(rc, mid + 1, r, L, R)) % MOD;
return res;
}
int main() {
scanf("%d%d%lld", &n, &m, &MOD);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
build(1, 1, n);
while (m--) {
int op, x, y; scanf("%d%d%d", &op, &x, &y);
if (op == 1) { ll k; scanf("%lld", &k); rangeMul(1, 1, n, x, y, k); }
else if (op == 2) { ll k; scanf("%lld", &k); rangeAdd(1, 1, n, x, y, k); }
else printf("%lld\n", query(1, 1, n, x, y));
}
return 0;
}
运行结果
输入: 5 4 571373
1 2 3 4 5
1 1 3 2
2 2 4 3
3 1 5
3 2 3
输出: 40
20
验证: [1,2,3,4,5] → 乘 [1,3] 得 [2,4,6,4,5]
→ [2,4] 加 3 得 [2,7,9,7,5]
[1,5] 之和 = 30 ?
题面样例请以洛谷为准;核心是双标记的更新顺序。