洛谷 P3373 模板 线段树2(线段树 懒惰传播)

题目大意:

有一串数an,允许对任一个区间做加法还有乘法,问某个区间的求和。

例如:

1 2 3 4 5 

进行操作[1,3]加1

数列变为:

2 3 4 4 5

进行操作[1,5]乘2

数列变为:

4 6 8 8 10

现在问[1,5]求和。

输出:36

解题思路:

数据量n=1e5 , 操作数m=1e5,暴力是万万不得。不用懒惰传播复杂度为O(n^2),妥妥tle

那我们很容易想到懒惰传播。问题是:这里的懒惰标签怎么打,我们知道假如只有乘法或者加法,那么这个懒惰传播还是好写的。对于只有加法的懒惰传播,每个节点的更新如下:

st[root]+=lazy[root]*(r-l+1)

其中 st表示线段树,lazy[root]表示我们打的懒惰标签,l和r分别为当前线段树遍历的区间。假如只有乘标签,那么我们每个节点的更新如下:

st[root]*=lazy[root]

但是,现在既有加法还有乘法,我们怎么更新一个节点呢?首先,我们知道肯定要做两个懒惰标签,其实我们在草稿纸模拟一下情况,标签有交替更新的情况,这是最难处理的。其中最难搞的是先遇到加法然后遇到乘法(大家可以排列组合一下++ ,+*,*+, **这四种更新标签的方法),我们怎么告诉节点应该是怎么更新呢?由乘法分配率: (a+b)*x=a*x+b*x,那么我们这里是不是应该可以在更新乘法的时候,让之前的加法标签更新一下呢。这个是本题最难想到的。我们每个节点的更新如下:

st[root]=lazymul[root]*st[root]+lazyadd[root]*(r-l+1)

其中lazymul[root] ,lazyadd[root]分别代表乘法和加法的懒标签。

同时我们可以写出在向下传递标签时候怎么更新(注意,标签怎么更新是和节点怎么更新有关!所以我们必须准确找到节点怎么更新,而怎么让节点更新也是懒惰传播最难的地方

modn是取模。left是往左走,right是往右走。

完整代码:

#include <bits/stdc++.h>
#define int long long
#define left(x) x<<1
#define right(x) (x<<1)+1
using namespace std;
int MODN;
const int MAXN=1e5+10;
int st[MAXN*4];
int lazyadd[MAXN*8];
int lazymul[MAXN*8];
int A[MAXN];

int modn(int a,int b){
	return ((a % b) + b) % b;
}
int build(int root,int l,int r){
	if(l==r)return st[root]=A[l];
	int m=l+(r-l)/2;
	int lv=build(left(root),l,m);
	int rv=build(right(root),m+1,r);
	return st[root]=lv+rv;
}
int upd1(int root,int l,int r,int ql,int qr,int val){
	if(lazyadd[root]!=0 || lazymul[root]!=1){
		st[root]=modn((lazymul[root]*st[root]+lazyadd[root]*(r-l+1)),MODN);
		lazyadd[left(root)]=modn(((lazyadd[left(root)]*lazymul[root])+lazyadd[root]),MODN);
		lazyadd[right(root)]=modn(((lazyadd[right(root)]*lazymul[root])+lazyadd[root]),MODN);
		
		lazymul[left(root)]=modn((lazymul[root]*lazymul[left(root)]),MODN);
		lazymul[right(root)]=modn((lazymul[root]*lazymul[right(root)]),MODN);
		
		lazyadd[root]=0;
		lazymul[root]=1;
	}
	if(l>=ql &&r<=qr){
		
		lazymul[left(root)]=modn((lazymul[left(root)]*val),MODN);
		lazyadd[left(root)]=modn((lazyadd[left(root)]*val),MODN);
		
		lazymul[right(root)]=modn((lazymul[right(root)]*val),MODN);
		lazyadd[right(root)]=modn((lazyadd[right(root)]*val),MODN);
		return st[root]=modn((st[root]*val),MODN);
	}
	if(l>qr||r<ql)return st[root];
	int m=l+(r-l)/2;
	int lf=upd1(left(root),l,m,ql,qr,val);
	int rg=upd1(right(root),m+1,r,ql,qr,val);
	return st[root]=modn((lf+rg),MODN);
}
int upd2(int root,int l,int r,int ql,int qr,int val){
	if(lazyadd[root]!=0 || lazymul[root]!=1){
		st[root]=modn((lazymul[root]*st[root]+lazyadd[root]*(r-l+1)),MODN);
		lazyadd[left(root)]=modn(((lazyadd[left(root)]*lazymul[root])+lazyadd[root]),MODN);
		lazyadd[right(root)]=modn(((lazyadd[right(root)]*lazymul[root])+lazyadd[root]),MODN);
		lazymul[left(root)]=modn((lazymul[root]*lazymul[left(root)]),MODN);
		lazymul[right(root)]=modn((lazymul[root]*lazymul[right(root)]),MODN);
		lazyadd[root]=0;
		lazymul[root]=1;
	}
	if(l>=ql && r<=qr){
		lazyadd[left(root)]=modn((lazyadd[left(root)]+val),MODN);
		lazyadd[right(root)]=modn((lazyadd[right(root)]+val),MODN);
		return st[root]=modn((st[root]+val*(r-l+1)),MODN);
	}
	if(l>qr || r<ql)return st[root];
	int m=l+(r-l)/2;
	int lv=upd2(left(root),l,m,ql,qr,val);
	int rv=upd2(right(root),m+1,r,ql,qr,val);
	return st[root]=modn((lv+rv),MODN);
}
int query(int root,int l,int r,int ql,int qr){
	if(lazyadd[root]!=0 || lazymul[root]!=1){
		st[root]=modn((lazymul[root]*st[root]+lazyadd[root]*(r-l+1)),MODN);
		lazyadd[left(root)]=modn(((lazyadd[left(root)]*lazymul[root])+lazyadd[root]),MODN);
		lazyadd[right(root)]=modn(((lazyadd[right(root)]*lazymul[root])+lazyadd[root]),MODN);
		
		lazymul[left(root)]=modn((lazymul[root]*lazymul[left(root)]),MODN);
		lazymul[right(root)]=modn((lazymul[root]*lazymul[right(root)]),MODN);
		
		lazyadd[root]=0;
		lazymul[root]=1;
	}
	if(l>=ql && r<=qr)return st[root];
	if(l>qr || r<ql)return 0;
	int m=l+(r-l)/2;
	int lv=query(left(root),l,m,ql,qr);
	int rv=query(right(root),m+1,r,ql,qr);
	return modn((lv+rv),MODN);
}
int32_t main(){
	int n,m;
	scanf("%lld%lld%lld",&n,&m,&MODN);
	for(int i=0;i<n;i++)
	{scanf("%lld",&A[i]);
	}
	build(1,0,n-1);
	memset(lazyadd,0,sizeof(lazyadd));
	for(int i=0;i<8*MAXN;i++)lazymul[i]=1;
	for(int i=0;i<m;i++){
		int c,x,y;
		scanf("%lld%lld%lld",&c,&x,&y);
		if(c==1){
			int val;
			scanf("%lld",&val);
			upd1(1,0,n-1,x-1,y-1,val);
		}
		else if(c==2){
			int val;
			scanf("%lld",&val);
			upd2(1,0,n-1,x-1,y-1,val);
			}else{
			int ans=query(1,0,n-1,x-1,y-1);
			printf("%lld\n",ans);
			}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值