线段树进阶(多懒标记)

不会基础先学这个线段树基础

我们以P3373 【模板】线段树 2来开始讲

简单来说题目是让我们执行3个操作:
区间加、区间乘,区间查询。
就是要用两个懒标记——一个add加法懒标记、一个mul乘法懒标记。
只有一点点与原来不一样。
更新懒标记时:
mul=mul*new_mul;
add=add*new_mul+new_add;
sum更新:
sum=sum*new_mul+(r-l+1)*new_add;
上代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define F(i,a,b) for(int i=a;i<=b;i++)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
const int N=1e6+5;
int a[N],sum[N<<2],lazy_add[N<<2],lazy_mul[N<<2];
int n,q,mod,op,x,y,k;
void pushup(int k)
{
	sum[k]=sum[ls(k)]+sum[rs(k)];
	return;
}
void update_son(int k,int l,int r,int add,int mul)//懒标记下放
{
	lazy_mul[k]*=mul;
	lazy_mul[k]%=mod;
	lazy_add[k]=lazy_add[k]*mul+add;
	lazy_add[k]%=mod;
	sum[k]*=mul;
	sum[k]%=mod;
	sum[k]+=(r-l+1)*add;
	sum[k]%=mod;
	return; 
}
void pushdown(int k,int l,int r)
{
	int mid=(l+r)>>1;
	update_son(ls(k),l,mid,lazy_add[k],lazy_mul[k]);
	update_son(rs(k),mid+1,r,lazy_add[k],lazy_mul[k]);
	lazy_add[k]=0;
	lazy_mul[k]=1;
	return;
}
void build(int k,int l,int r)
{
	lazy_add[k]=0;
	lazy_mul[k]=1;
	if(l==r)
	{
		sum[k]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(ls(k),l,mid);
	build(rs(k),mid+1,r);
	pushup(k);
	return;
}
int query(int L,int R,int k,int l,int r)
{
	if(L<=l&&r<=R)
	{
		return sum[k];
	}
	int res=0;
	int mid=(l+r)>>1;
	pushdown(k,l,r);
	if(L<=mid)
	{
		res+=query(L,R,ls(k),l,mid);
		res%=mod;
	}
	if(R>mid)
	{
		res+=query(L,R,rs(k),mid+1,r);
		res%=mod;
	}
	return res;
}
void update(int L,int R,int add,int mul,int k,int l,int r)
{
	if(L<=l&&r<=R)
	{
		update_son(k,l,r,add,mul);
		return;
	}
	int mid=(l+r)>>1;
	pushdown(k,l,r);
	if(L<=mid)
	{
		update(L,R,add,mul,ls(k),l,mid);
	}
	if(R>mid)
	{
		update(L,R,add,mul,rs(k),mid+1,r);
	}
	pushup(k);
	return;
}
signed main()
{
	cin>>n>>q>>mod;
	F(i,1,n)
	{
		cin>>a[i];
	}
	build(1,1,n);
	while(q--)
	{
		cin>>op;
		if(op==1)
		{
			cin>>x>>y>>k;
			update(x,y,0,k,1,1,n);
		}
		if(op==2)
		{
			cin>>x>>y>>k;
			update(x,y,k,1,1,1,n);
		}
		if(op==3)
		{
			cin>>x>>y;
			cout<<query(x,y,1,1,n)<<'\n';
		}
	}
	return 0;
}

练练手

P1253 扶苏的问题
简单来说题目是让我们执行3个操作:
区间加、区间赋值,区间查询。
赋值可以当成乘0加k。
完整代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define F(i,a,b) for(int i=a;i<=b;i++)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define INF 1e18
const int N=(1e6+7);
int a[N],t[N<<2],lazy_add[N<<2],lazy_mul[N<<2];
int n,q,op,x,y,k;
void pushup(int k)
{
	t[k]=max(t[ls(k)],t[rs(k)]);
	return;
}
void update_son(int k,int l,int r,int add,int mul)
{
	lazy_mul[k]*=mul;
	lazy_add[k]=lazy_add[k]*mul+add;
	t[k]*=mul;
	t[k]+=add;
	return; 
}
void pushdown(int k,int l,int r)
{
	int mid=(l+r)>>1;
	update_son(ls(k),l,mid,lazy_add[k],lazy_mul[k]);
	update_son(rs(k),mid+1,r,lazy_add[k],lazy_mul[k]);
	lazy_add[k]=0;
	lazy_mul[k]=1;
	return;
}
void build(int k,int l,int r)
{
	lazy_add[k]=0;
	lazy_mul[k]=1;
	if(l==r)
	{
		t[k]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(ls(k),l,mid);
	build(rs(k),mid+1,r);
	pushup(k);
	return;
}
int query(int L,int R,int k,int l,int r)
{
	if(L<=l&&r<=R)
	{
		return t[k];
	}
	int res=-1e18;
	int mid=(l+r)>>1;
	pushdown(k,l,r);
	if(L<=mid)
	{
		res=max(res,query(L,R,ls(k),l,mid));
	}
	if(R>mid)
	{
		res=max(res,query(L,R,rs(k),mid+1,r));
	}
	return res;
}
void update(int L,int R,int add,int mul,int k,int l,int r)
{
	if(L<=l&&r<=R)
	{
		update_son(k,l,r,add,mul);
		return;
	}
	int mid=(l+r)>>1;
	pushdown(k,l,r);
	if(L<=mid)
	{
		update(L,R,add,mul,ls(k),l,mid);
	}
	if(R>mid)
	{
		update(L,R,add,mul,rs(k),mid+1,r);
	}
	pushup(k);
	return;
}
signed main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>q;
	F(i,1,n)
	{
		cin>>a[i];
	}
	build(1,1,n);
	while(q--)
	{
		cin>>op;
		if(op==1)
		{
			cin>>x>>y>>k;
			update(x,y,k,0,1,1,n);
		}
		if(op==2)
		{
			cin>>x>>y>>k;
			update(x,y,k,1,1,1,n);
		}
		if(op==3)
		{
			cin>>x>>y;
			cout<<query(x,y,1,1,n)<<'\n';
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值