题意: 稍加修改的线段树模板题。
题解: 由于乘法和加法两种修改,$lazy$标记不能确定的指出当前待修改的操作是乘法在前还是加法在前,亦或是两者混合存在,所以我们标记两个 $lazy:add$ 和 $mul$。更新的时候$add$更新为$add \times mul’+add’$,$mul$更新为$mul \times mul’+add’$,$sum$更新为$sum \times mul + add*(u.len)$
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, m, q[N];
ll mod;
struct Node {
int l, r;
ll sum, add, mul;
}tr[N << 2];
void pushup(int u) {
tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % mod;
}
void eval(Node &t, ll add, ll mul) {
t.sum = (t.sum * mul + (t.r - t.l + 1) * add) % mod;
t.mul = t.mul * mul % mod;
t.add = (t.add * mul + add) % mod;
}
void pushdown(int u) {
eval(tr[u << 1], tr[u].add, tr[u].mul);
eval(tr[u << 1 | 1], tr[u].add, tr[u].mul);
tr[u].add = 0, tr[u].mul = 1;
}
void build(int u, int l, int r) {
tr[u] = {l, r, 0, 0, 1};
if(l == r) {
tr[u].sum = q[l];
return ;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, ll add, ll mul) {
if(tr[u].l >= l && tr[u].r <= r) {
eval(tr[u], add, mul);
return ;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u << 1, l, r, add, mul);
if(r > mid) modify(u << 1 | 1, l, r, add, mul);
pushup(u);
}
ll query(int u, int l, int r) {
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
ll res = 0;
if(l <= mid) res += query(u << 1, l, r);
if(r > mid) res += query(u << 1 | 1, l, r);
return res % mod;
}
int main()
{
scanf("%d%lld", &n, &mod);
for(int i = 1; i <= n; i++) scanf("%d", &q[i]);
build(1, 1, n);
scanf("%d", &m);
while(m--) {
int op, l, r;
ll c;
scanf("%d%d%d", &op, &l, &r);
if(op == 1) {
scanf("%lld", &c);
c %= mod;
modify(1, l, r, 0, c);
}
else if(op == 2) {
scanf("%lld", &c);
c %= mod;
modify(1, l, r, c, 1);
}
else printf("%lld\n", query(1, l, r));
}
return 0;
}