题解 P3373 【【模板】线段树 2】
pupuvovovovovo · · 题解
线段树多个lazy标记的应用。
每个点上的标记表示自己儿子要乘或加上多少。
标记下放时,儿子节点的add标记先乘上父亲节点的乘标记再加上父亲节点的加标记(因为父亲节点的加标记已经乘上了一些乘标记了,不需要再乘一次)
儿子节点的值同上。
儿子节点的乘标记直接乘上父亲节点的乘标记。
修改值的时候要根据下放标记与修改值的先后次序进行微调。
就是这样。
(你拷贝代码会WA的)
#include<bits/stdc++.h>
using namespace std;
struct xds{
int l,r;
long long val,add,time;
};
int n,m,p,i,opt,x,y,k,ans;
long long a[100001];
xds t[400001];
void make(int l,int r,int k){
t[k].time=1;
t[k].l=l;
t[k].r=r;
if (l==r){
t[k].val=a[l];
return;
}
int mid=(l+r)/2;
make(l,mid,k*2);
make(mid+1,r,k*2+1);
t[k].val=(t[k*2].val+t[k*2+1].val)%p;
}
void down(int k){
if (t[k].l<t[k].r){
t[k*2].add=(t[k*2].add*t[k].time+t[k].add)%p;
t[k*2+1].add=(t[k*2+1].add*t[k].time+t[k].add)%p;
t[k*2].time=(t[k*2].time*t[k].time)%p;
t[k*2+1].time=(t[k*2+1].time*t[k].time)%p;
t[k*2].val=(t[k*2].val*t[k].time+t[k].add*(t[k*2].r-t[k*2].l+1))%p;
t[k*2+1].val=(t[k*2+1].val*t[k].time+t[k].add*(t[k*2+1].r-t[k*2+1].l+1))%p;
}
t[k].add=0;
t[k].time=1;
}
void ch_a(int k,int a){
if (t[k].r<x||t[k].l>y) return;
if (t[k].l>=x&&t[k].r<=y){
t[k].add=(t[k].add+a)%p;
t[k].val=(t[k].val+a*(t[k].r-t[k].l-1))%p;
return;
}
if (t[k].add!=0||t[k].time!=1) down(k);
ch_t(k*2,a);
ch_t(k*2+1,a);
t[k].val=(t[k*2].val+t[k*2+1].val)%p;
}
void ch_t(int k,int a){
if (t[k].r<x||t[k].l>y) return;
if (t[k].l>=x&&t[k].r<=y){
t[k].time=(t[k].time*a)%p;
t[k].add=(t[k].add*a)%p;
t[k].val=(t[k].val*a)%p;
return;
}
if (t[k].add!=0||t[k].time!=1) down(k);
ch_t(k*2,a);
ch_t(k*2+1,a);
t[k].val=(t[k*2].val+t[k*2+1].val)%p;
}
void ask(int k){
if (t[k].r<x||t[k].l>y) return;
if (t[k].l>=x&&t[k].r<=y){
ans=(ans+t[k].val)%p;
return;
}
if (t[k].add!=0||t[k].time!=1) down(k);
ask(k*2);
ask(k*2+1);
}
int main(){
scanf("%d%d%d",&n,&m,&p);
for (i=1;i<=n;i++) scanf("%d",&a[i]);
make(1,n,1);
for (i=1;i<=m;i++){
scanf("%d%d%d",&opt,&x,&y);
switch (opt){
case 1:{
scanf("%d",&k);
ch_t(1,k%p);
break;
}
case 2:{
scanf("%d",&k);
ch_a(1,k%p);
break;
}
case 3:{
ans=0;
ask(1);
printf("%d\n",ans);
break;
}
}
}
return 0;
}