线段树同时实现区间加法和乘法,因为加法乘法都涉及到了动态修改,因此我们需要两个lazy标记,我们记为add,mul,但是在pushdown的时候就会出现一个问题,乘法对之前做过的加法标记是有影响的,如果先下推加法标记,那么就忽略乘法对结果的影响,导致错误的答案
当我们需要将标记下推的时候,正确的操作应该是将当前区间的add也乘上mul,下推保证结果正确,而在添加mul标记的时候之前这个位置所拥有的add标记也要乘上mul,这样保证在做乘法操作的时候,加法保持正确。加法就按原本的操作,无需改动。
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6 + 10;
struct SegmentTree { int l,r; LL sum,mul = 1,add;}tree[maxn<<2];
LL num[maxn], mod;
void pushup(int id){
tree[id].sum = (tree[id<<1].sum + tree[id<<1|1].sum) % mod;
}
void pushdown(int id){
if (tree[id].mul != 1 || tree[id].add){
int l = tree[id].l,r = tree[id].r,mid = (tree[id].l + tree[id].r) >> 1;
tree[id<<1].mul = (tree[id<<1].mul*tree[id].mul) % mod;
tree[id<<1|1].mul = (tree[id<<1|1].mul*tree[id].mul) % mod;
tree[id<<1].add = (tree[id<<1].add*tree[id].mul) % mod;
tree[id<<1|1].add = (tree[id<<1|1].add*tree[id].mul) % mod;
tree[id<<1].sum = (tree[id<<1].sum*tree[id].mul) % mod;
tree[id<<1|1].sum = (tree[id<<1|1].sum*tree[id].mul) % mod;
tree[id].mul = 1;
tree[id<<1].add = (tree[id<<1].add+tree[id].add) % mod;
tree[id<<1|1].add = (tree[id<<1|1].add+tree[id].add) % mod;
tree[id<<1].sum = (tree[id<<1].sum+(mid-l+1)*tree[id].add) % mod;
tree[id<<1|1].sum = (tree[id<<1|1].sum+(r-mid)*tree[id].add) % mod;
tree[id].add = 0;
}
}
void build(int id,int l,int r){
tree[id].l = l;
tree[id].r = r;
if (l == r) {
tree[id].sum = num[l];
return;
}
int mid = (l + r) >> 1;
build(id<<1, l, mid);
build(id<<1|1, mid+1, r);
pushup(id);
}
void add(int id,int ql,int qr,int c){
int l = tree[id].l,r = tree[id].r;
if (ql <= l && r <= qr){
tree[id].add = (tree[id].add + c) % mod;
tree[id].sum = (tree[id].sum + (r-l+1)*c) % mod;
return;
}
int mid = (r + l) >> 1;
pushdown(id);
if (ql <= mid) add(id<<1, ql, qr, c);
if (qr > mid) add(id<<1|1, ql, qr, c);
pushup(id);
}
void mul(int id,int ql,int qr,int c){
int l = tree[id].l,r = tree[id].r;
if (ql <= l && r <= qr){
tree[id].mul = tree[id].mul*c % mod;
tree[id].add = tree[id].add*c % mod;
tree[id].sum = tree[id].sum*c % mod;
return;
}
int mid = (l + r) >> 1;
pushdown(id);
if (ql <= mid) mul(id<<1, ql, qr, c);
if (qr > mid) mul(id<<1|1, ql, qr, c);
pushup(id);
}
LL query1(int id,int ql,int qr){
int l = tree[id].l,r = tree[id].r;
if (ql <= l && r <= qr) return tree[id].sum;
pushdown(id);
int mid = (l + r) >> 1;
LL sum = 0;
if (mid >= qr) sum = (sum + query1(id<<1, ql, qr)) % mod;
else if (ql >= mid+1) sum = (sum + query1(id<<1|1, ql, qr)) % mod;
else{
sum = (sum + query1(id<<1, ql, qr)) % mod;
sum = (sum + query1(id<<1|1, ql, qr)) % mod;
}
return sum%mod;
}
int main(){
// freopen("/Users/chutong/data.txt", "r", stdin);
int n,m,x,y,k;
scanf("%d%d%lld",&n,&m,&mod);
for (int i=1; i<=n; i++)
scanf("%lld",&num[i]);
build(1, 1, n);
int opt;
for (int i=1; i<=m; i++) {
scanf("%d%d%d",&opt,&x,&y);
if (opt == 1){
scanf("%d",&k);
mul(1, x, y, k);
}else if (opt == 2){
scanf("%d",&k);
add(1, x, y, k);
}else{
printf("%lld\n",query1(1, x, y));
}
}
return 0;
}