题意
思路
- 这题我们既要维护区间加法,又要维护区间乘法,也就是我们要维护两个懒标:add、mul。
- 这题给我的启发就是当我有多个懒标的时候,我们要考虑懒标之间的优先级变化。
- 这题为了方便懒标更新,我们应该:先更新乘懒标 mul,再更新加懒标 add。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define ls (k << 1)
#define rs (k << 1 | 1)
const int N = 1e5 + 10;
int n, m, p;
int a[N];
struct Tree
{
int l, r, sum, add, mul;
} tr[N * 4];
void push_up(int k)
{
tr[k].sum = (tr[ls].sum + tr[rs].sum) % p;
}
void cal(Tree & t, int add, int mul)
{
t.sum = (1LL * t.sum * mul + 1LL * (t.r - t.l + 1) * add) % p;
t.mul = 1LL * t.mul * mul % p;
t.add = (1LL * t.add * mul + add) % p;
}
void push_down(int k)
{
cal(tr[ls], tr[k].add, tr[k].mul);
cal(tr[rs], tr[k].add, tr[k].mul);
tr[k].add = 0, tr[k].mul = 1;
}
void build(int k, int l, int r)
{
tr[k] = { l, r, 0, 0, 1};
if (l == r)
{
tr[k] = { l, r, a[l], 0, 1 };
return;
}
int md = (l + r) >> 1;
build(ls, l, md);
build(rs, md + 1, r);
push_up(k);
}
void modify(int k, int l, int r, int add, int mul)
{
if (tr[k].l >= l && tr[k].r <= r)
{
cal(tr[k], add, mul);
return;
}
push_down(k);
int md = (tr[k].l + tr[k].r) >> 1;
if (l <= md) modify(ls, l, r, add, mul);
if (r > md) modify(rs, l, r, add, mul);
push_up(k);
}
int query(int k, int l, int r)
{
if (tr[k].l >= l && tr[k].r <= r) return tr[k].sum;
push_down(k);
int md = (tr[k].l + tr[k].r) >> 1;
int sum = 0;
if (l <= md) sum = query(ls, l, r);
if (r > md) sum = (sum + query(rs, l, r)) % p;
return sum;
}
int main()
{
scanf("%d %d", &n, &p);
for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);
build(1, 1, n);
scanf("%d", &m);
int op, l, r, d;
while (m --)
{
scanf("%d %d %d", &op, &l, &r);
if (op == 1)
{
scanf("%d", &d);
modify(1, l, r, 0, d);
}
else if (op == 2)
{
scanf("%d", &d);
modify(1, l, r, d, 1);
}
else
{
printf("%d\n", query(1, l, r));
}
}
return 0;
}