luogu 3373 线段树懒标记维护乘与加

luogu 3373
线段树懒标记维护乘与加

每个节点的sum,一定是这个点所带的懒标记经过所有运算后最终的sum值,这种题目做起来一定要系统化,必须定好标准,决不能混乱。

每次询问到一个点,如果这个点不用深入下去,则直接打上标记即可,同时更新这个点的sum值,否则的话需要把标记传给它的左右儿子,然后它的标记清0。一定要养成无论是修改还是询问一旦进入子树必须先push_down的好习惯,最后别忘了结束位置再根据它的左右节点sum值update一下。

一个要注意的地方,由于有两种标记,故也要定好两种标记的优先级。
乘法优于加法,即如果一个地方打了一个乘法标记,那么它的乘法标记、加法标记、sum值都得乘这个标记值。如果打了一个加法标记,则只需要修改这个点的加法标记与sum值,无须修改乘法标记,切勿优先标准混乱。

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL N=110000;
LL n,m,p,op,x,y,k,mo;
struct node{
	LL sum,add,mul;
}t[N*4];
void update(LL w){
	t[w].sum=(t[w*2].sum+t[w*2+1].sum)%mo;
}
void down_push(LL w,LL l,LL r){
	LL mid=(l+r)/2;
	t[w*2].mul=(t[w*2].mul*t[w].mul)%mo;
	t[w*2].add=(t[w*2].add*t[w].mul+t[w].add)%mo;
	t[w*2].sum=(t[w*2].sum*t[w].mul+t[w].add*(mid-l+1))%mo;
	t[w*2+1].mul=(t[w*2+1].mul*t[w].mul)%mo;
	t[w*2+1].add=(t[w*2+1].add*t[w].mul+t[w].add)%mo;
	t[w*2+1].sum=(t[w*2+1].sum*t[w].mul+t[w].add*(r-mid))%mo;
	t[w].mul=1;
	t[w].add=0;
}
void build(LL w,LL l,LL r){
	t[w].mul=1;
	if(l==r){
		cin>>t[w].sum;
		return;
	}
	LL mid=(l+r)/2;
	build(w*2,l,mid);
	build(w*2+1,mid+1,r);
	update(w);
}
void modifymul(LL w,LL l,LL r,LL ll,LL rr,LL z){
	if(ll<=l&&rr>=r){
		t[w].mul=t[w].mul*z%mo;
		t[w].sum=t[w].sum*z%mo;
		t[w].add=t[w].add*z%mo;
		return;
	}
	down_push(w,l,r);
	LL mid=(l+r)/2;
	if(ll<=mid)modifymul(w*2,l,mid,ll,rr,z);
	if(rr>mid)modifymul(w*2+1,mid+1,r,ll,rr,z);
	update(w);
}
void modifyadd(LL w,LL l,LL r,LL ll,LL rr,LL z){
	if(ll<=l&&rr>=r){
		t[w].sum=(t[w].sum+z*(r-l+1))%mo;
		t[w].add=(t[w].add+z)%mo;
		return;
	}
	down_push(w,l,r);
	LL mid=(l+r)/2;
	if(ll<=mid)modifyadd(w*2,l,mid,ll,rr,z);
	if(rr>mid)modifyadd(w*2+1,mid+1,r,ll,rr,z);
	update(w);
}
LL query(LL w,LL l,LL r,LL ll,LL rr){
	if(ll<=l&&rr>=r)return t[w].sum;
	down_push(w,l,r);
	LL ans=0;
	LL mid=(l+r)/2;
	if(ll<=mid)ans+=query(w*2,l,mid,ll,rr);
	if(rr>mid)ans+=query(w*2+1,mid+1,r,ll,rr);
	return ans%mo;
}
int main(){
	cin>>n>>m>>mo;
	build(1,1,n);
	while(m--){
		cin>>op;
		if(op==1){
			cin>>x>>y>>k;
			modifymul(1,1,n,x,y,k);
		}
		if(op==2){
			cin>>x>>y>>k;
			modifyadd(1,1,n,x,y,k);
		}
		if(op==3){
			cin>>x>>y;
			cout<<query(1,1,n,x,y)<<endl;
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值