题目大意:有$n$个数,有$m$个操作,有三种:
- $1;l;r;x:$把区间$[l,r]$内的数乘上$x$
- $2;l;r;x:$把区间$[l,r]$内的数加上$x$
- $3;l;r:$询问区间$[l,r]$的和,对$p$取模
(线段树2就是先读入$n;m;p$,再读入序列;本题是先读入$n;p$,读入序列,再读入$m$,双倍经验)
题解:线段树,把$lazy\_tag$变成两个,分别记录区间加和区间乘,注意乘法的优先级比加法高
卡点:无(我以前写的是什么代码啊?)
C++ Code:
#include <cstdio> #define maxn 100010 << 2 long long V[maxn], cov[maxn], tg[maxn]; int n, m; int s[maxn], L, R; long long p, x; void update(int rt) { V[rt] = (V[rt << 1] + V[rt << 1 | 1]) % p; } void build(int rt, int l, int r) { cov[rt] = 1; if (l == r) { V[rt] = s[l] % p; return ; } int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); update(rt); } void pushdown(int rt, long long len) { long long &Cov = cov[rt], &Tg = tg[rt]; V[rt << 1] = (V[rt << 1] * Cov + Tg * (len + 1 >> 1)) % p; V[rt << 1 | 1] = (V[rt << 1 | 1] * Cov + Tg * (len >> 1)) % p; cov[rt << 1] = (cov[rt << 1] * Cov) % p; cov[rt << 1 | 1] = (cov[rt << 1 | 1] * Cov) % p; tg[rt << 1] = (tg[rt << 1] * Cov + Tg) % p; tg[rt << 1 | 1] = (tg[rt << 1 | 1] * Cov + Tg) % p; Cov = 1, Tg = 0; } void add1(int rt, int l, int r) { if (L <= l && R >= r) { V[rt] = (V[rt] * x) % p; cov[rt] = (cov[rt] * x) % p; tg[rt] = (tg[rt] * x) % p; return ; } int mid = l + r >> 1; if (cov[rt] != 1 || tg[rt]) pushdown(rt, r - l + 1); if (L <= mid) add1(rt << 1, l, mid); if (R > mid) add1(rt << 1 | 1, mid + 1, r); update(rt); } void add2(int rt, int l, int r) { if (L <= l && R >= r) { V[rt] = (V[rt] + x * (r - l + 1ll)) % p; tg[rt] = (tg[rt] + x) % p; return ; } int mid = l + r >> 1; if (cov[rt] != 1 || tg[rt]) pushdown(rt, r - l + 1); if (L <= mid) add2(rt << 1, l, mid); if (R > mid) add2(rt << 1 | 1, mid + 1, r); update(rt); } long long ask(int rt, int l, int r) { if (L <= l && R >= r) return V[rt] % p; int mid = l + r >> 1; long long ans = 0; if (cov[rt] != 1 || tg[rt]) pushdown(rt, r - l + 1); if (L <= mid) ans = ask(rt << 1, l, mid); if (R > mid) ans = (ans + ask(rt << 1 | 1, mid + 1, r)) % p; return ans; } int main() { scanf("%d%lld", &n, &p); for (int i = 1; i <= n; i++) scanf("%d", s + i); build(1, 1, n); scanf("%d", &m); while (m --> 0) { long long op; scanf("%lld%d%d", &op, &L, &R); switch (op) { case 1: { scanf("%lld", &x); add1(1, 1, n); break; } case 2: { scanf("%lld", &x); add2(1, 1, n); break; } default: printf("%lld ", ask(1, 1, n)); } } return 0; }