题解
吐血
线段树有了懒标记,不管写多少次都那么要命
添加两个懒标记:add 和 mul
对于每个区间来说都有以下公式:区间的值 = ( 区间和 + 新加的数 ) * 新乘的数
详细见代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int N=1e6+10;
int n,m,k,p;
ll a[N];
ll sum[N<<2];
ll add[N<<2],mul[N<<2];//懒标记
void pushup(int rt){
sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%p;
}
void pushdown(int rt,int len){//(sum+add)*mul
//更新左儿子 左儿子区间长度为len+1>>1
sum[rt<<1]=(sum[rt<<1]*mul[rt]+add[rt]*(len+1>>1))%p;
mul[rt<<1]=(mul[rt<<1]*mul[rt])%p;
add[rt<<1]=(add[rt<<1]*mul[rt]+add[rt])%p;
//更新右儿子 右儿子区间长度为len>>1
sum[rt<<1|1]=(sum[rt<<1|1]*mul[rt]+add[rt]*(len>>1))%p;
mul[rt<<1|1]=(mul[rt<<1|1]*mul[rt])%p;
add[rt<<1|1]=(add[rt<<1|1]*mul[rt]+add[rt])%p;
//更新父亲
mul[rt]=1;
add[rt]=0;
}
void build(int l,int r,int rt){
mul[rt]=1;
if(l==r){
cin>>sum[rt];//叶节点即 sum[rt]=a[l]
return ;
}
int mid=l+r>>1;
build(lson);
build(rson);
pushup(rt);
}
void Add(int x,int y,ll val,int l,int r,int rt){
if(x<=l && r<=y){
add[rt]=(add[rt]+val)%p;//标记一下增加的数
sum[rt]=(sum[rt]+val*(r-l+1))%p;
return;
}
pushdown(rt,r-l+1);
int mid=l+r>>1;
if(x<=mid)Add(x,y,val,lson);
if(y>mid)Add(x,y,val,rson);
pushup(rt);
}
void Multiple(int x,int y,ll val,int l,int r,int rt){
if(x<=l && r<=y){
mul[rt]=(mul[rt]*val)%p;
sum[rt]=(sum[rt]*val)%p;
add[rt]=(add[rt]*val)%p;// (sum+add)*mul
return ;
}
pushdown(rt,r-l+1);
int mid=l+r>>1;
if(x<=mid)Multiple(x,y,val,lson);
if(y>mid)Multiple(x,y,val,rson);
pushup(rt);
}
ll query(int x,int y,int l,int r,int rt){
if(x<=l && r<=y){
return sum[rt];
}
pushdown(rt,r-l+1);
int mid=l+r>>1;
ll res=0;
if(x<=mid)res+=query(x,y,lson);
if(y>mid)res+=query(x,y,rson);
pushup(rt);
return (res%p);
}
int main(){
cin>>n>>p;
build(1,n,1);
cin>>m;
for (int i = 1,op,x,y,z; i <= m; ++i) {
cin>>op>>x>>y;
if(op==1){
cin>>z;
Multiple(x,y,z,1,n,1);
}else if(op==2){
cin>>z;
Add(x,y,z,1,n,1);
}else{
cout <<query(x,y,1,n,1) << endl;
}
}
return 0;
}