裸的不能再裸的线段树,拿这个练练手,防手生。
需要两个标记,加和乘。列个式子看一看就能明白:当新标记是加时,直接加到原加标记中,新标记是乘时,既要乘到原乘标记中,还要乘到加标记中。这样在pushdown时先下放乘标记再下放加标记即可。
需要注意的是不要忘了在修改时是要pushdown两遍的,其实第二次说是maintain更合适,因为只需要再计算一次sum[p] = sum[lc]+sum[rc],直接写一句话也可以。
#include <cstdio>
#include <algorithm>
#include <cstring>
#define M 100005
#define lc p<<1
#define rc p<<1|1
#define mid ((l+r)>>1)
typedef long long L;
using namespace std;
int x, a, b;
L n, m, P, c;
L sum[M<<2], mul[M<<2], plu[M<<2];
void build(int p, int l, int r){
mul[p] = 1;
if(l == r){
scanf("%lld", sum+p);
sum[p] %= P;
return ;
}
build(lc, l, mid);
build(rc, mid+1, r);
sum[p] = sum[lc] + sum[rc];
sum[p] %= P;
}
L cal(int p, int len){
return ((sum[p]*mul[p]%P)+plu[p]*len)%P;
}
void pushdown(int p, int l, int r){
mul[lc] = mul[lc]*mul[p]%P;
mul[rc] = mul[rc]*mul[p]%P;
plu[lc] = plu[lc]*mul[p]%P;
plu[rc] = plu[rc]*mul[p]%P;
plu[lc] = plu[lc]+plu[p]%P;
plu[rc] = plu[rc]+plu[p]%P;
sum[p] = ((cal(lc, mid-l+1)+cal(rc, r-mid)))%P;
mul[p] = 1; plu[p] = 0;
}
void mult(int p, int l, int r){
if(a <= l && r <= b){
mul[p] = mul[p]*c%P;
plu[p] = plu[p]*c%P;
return ;
}
pushdown(p, l, r);
if(a <= mid) mult(lc, l, mid);
if(b > mid) mult(rc, mid+1, r);
sum[p] = ((cal(lc, mid-l+1)+cal(rc, r-mid)))%P; //NO.2 “pushdown”
}
void plus(int p, int l, int r){
if(a <= l && r <= b){
plu[p] = (plu[p]+c)%P;
return ;
}
pushdown(p, l, r);
if(a <= mid) plus(lc, l, mid);
if(b > mid) plus(rc, mid+1, r);
sum[p] = ((cal(lc, mid-l+1)+cal(rc, r-mid)))%P;
}
L query(int p, int l, int r){
if(a <= l && r <= b){
return cal(p, r-l+1);
}
pushdown(p, l, r);
L res = 0;
if(a <= mid) res = (res+query(lc, l, mid))%P;
if(b > mid) res = (res+query(rc, mid+1, r))%P;
return res;
}
int main()
{
scanf("%lld %lld", &n, &P);
build(1, 1, n);
scanf("%lld", &m);
while(m--){
scanf("%d %d %d", &x, &a, &b);
if(x == 1){
scanf("%lld", &c);
mult(1, 1, n);
}
if(x == 2){
scanf("%lld", &c);
plus(1, 1, n);
}
if(x == 3){
printf("%lld\n", query(1, 1, n));
}
}
return 0;
}