对于吉司机线段树下传懒标记的顺序的解释

对于吉司机线段树下传懒标记的顺序的解释

哇,标题怎么这么长

吉司机线段树

是线段树的一种,可用于维护区间 max ⁡ / min ⁡ \max/\min max/min 操作,即把原数组 a i a_i ai 变为 max ⁡ ( a i , x ) \max(a_i,x) max(ai,x) 或 min ⁡ ( a i , x ) \min(a_i,x) min(ai,x)。

以洛谷题目P10639 最假女选手为例,需要存的 lazytag(懒标记)为加法、区间 max ⁡ \max max 和 min ⁡ \min min。

那么我们下传懒标记(pushdown)操作中,如何确定下传顺序?

首先显然地,对于 min ⁡ \min min 和 max ⁡ \max max 操作,它们是同级的,先后顺序无所谓。

下文以 M M M 操作代替 min ⁡ \min min 和 max ⁡ \max max 操作,因为这俩太长了,懒得打。

那么问题就是加法和 M M M 操作的下传顺序问题。

下传顺序的解释

以 M M M 操作中的 max ⁡ \max max 为例子,即 a i = max ⁡ ( a i , x ) a_i=\max(a_i,x) ai=max(ai,x)。

假设我们有两个操作:

  • 加上 x x x
  • a i = max ⁡ ( a i , y ) a_i=\max(a_i,y) ai=max(ai,y)

当前元素为 a i a_i ai。

首先如果 a i ≥ y a_i\ge y ai≥y,那么 max ⁡ \max max 操作的结果是 a i = max ⁡ ( a i , y ) = a i a_i=\max(a_i,y)=a_i ai=max(ai,y)=ai,发现是无意义的,可以省略,直接加法。

现在考虑 max ⁡ \max max 操作会影响元素的情况。

不难发现 x x x 与 a i , y a_i,y ai,y 的相对大小是不影响的,所以只要考虑 a i a_i ai 和 y y y 的大小关系。

而上文分析过了,所以只要考虑 a i < y a_i<y ai<y 的情况。

现在证明为什么先下传加法懒标记的顺序是对的。

我们以一种情况举例子,其他可以自行尝试。

我们假设 a i < y < x a_i<y<x ai<y<x。

我们画个数轴理解下。

这是原操作先 max ⁡ \max max 后加法的结果,发现最后位置在 x x x 那。

这是先加法再 max ⁡ \max max 后的结果,发现最后结果是一样的。

所以先 pushdown 加法的 tag 再 pushdown M M M 操作的 tag 是对的。

例题都给了,就给个代码吧

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
#define FUP(i,x,y) for(auto (i)=(x);(i)<=(y);++(i))
#define FDW(i,x,y) for(auto (i)=(x);(i)>=(y);--(i))
inline void Rd(auto &num);
const int N=5e5+5;
const ljl inf=1e18;
int n,a[N],m;
namespace SMT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	struct NODE{
		ljl sum,lz,mx,mx2,cmx,mn,mn2,cmn,tmx,tmn,l,r;
	}node[N*4];
	void pushup(int p)
	{
		node[p].sum=node[lc].sum+node[rc].sum;
		if(node[lc].mx==node[rc].mx)
		{
			node[p].mx=node[lc].mx;node[p].cmx=node[lc].cmx+node[rc].cmx;
			node[p].mx2=max(node[lc].mx2,node[rc].mx2);
		}
		else
		{
			if(node[lc].mx<node[rc].mx)
			{
				node[p].mx=node[rc].mx;node[p].cmx=node[rc].cmx;
				node[p].mx2=max(node[lc].mx,node[rc].mx2);
			}
			else
			{
				node[p].mx=node[lc].mx;node[p].cmx=node[lc].cmx;
				node[p].mx2=max(node[lc].mx2,node[rc].mx);
			}
		}
		if(node[lc].mn==node[rc].mn)
		{
			node[p].mn=node[lc].mn;node[p].cmn=node[lc].cmn+node[rc].cmn;
			node[p].mn2=min(node[lc].mn2,node[rc].mn2);
		}
		else
		{
			if(node[lc].mn<node[rc].mn)
			{
				node[p].mn=node[lc].mn;node[p].cmn=node[lc].cmn;
				node[p].mn2=min(node[lc].mn2,node[rc].mn);
			}
			else
			{
				node[p].mn=node[rc].mn;node[p].cmn=node[rc].cmn;
				node[p].mn2=min(node[rc].mn2,node[lc].mn);
			}
		}
		return;
	}
	void bld(int l,int r,int p)
	{
		node[p].l=l;node[p].r=r;node[p].tmx=-inf;node[p].tmn=inf;
		if(l==r)
		{
			node[p].mx=node[p].mn=node[p].sum=a[l];
			node[p].cmn=node[p].cmx=1;
			node[p].mx2=-inf;node[p].mn2=inf;
			return;
		}
		int mid=(l+r)/2;
		bld(l,mid,lc);bld(mid+1,r,rc);
		pushup(p);
		return;
	}
	void pushsum(int p,ljl val)
	{
		node[p].sum+=(node[p].r-node[p].l+1)*val;
		node[p].mx+=val;node[p].mn+=val;
		if(node[p].mx2!=-inf)node[p].mx2+=val;
		if(node[p].mn2!=inf)node[p].mn2+=val;
		if(node[p].tmx!=-inf)node[p].tmx+=val;
		if(node[p].tmn!=inf)node[p].tmn+=val;
		node[p].lz+=val;
		return;
	}
	void pushmin(int p,ljl val)
	{
		if(node[p].mx<=val)return;
		node[p].sum+=(val-node[p].mx)*node[p].cmx;
		if(node[p].mn2==node[p].mx)
			node[p].mn2=val;
		if(node[p].mn==node[p].mx)node[p].mn=val;
		node[p].tmx=min(val,node[p].tmx);node[p].mx=val;node[p].tmn=val;
		return;
	}
	void pushmax(int p,ljl val)
	{
		if(node[p].mn>=val)return;
		node[p].sum+=(val-node[p].mn)*node[p].cmn;
		if(node[p].mx2==node[p].mn)
			node[p].mx2=val;
		if(node[p].mx==node[p].mn)node[p].mx=val;
		node[p].tmn=max(node[p].tmn,val);node[p].mn=val;node[p].tmx=val;
		return;
	}
	void pushdown(int p)
	{
		if(node[p].l==node[p].r)return;
		if(node[p].lz)
		{
			pushsum(lc,node[p].lz);
			pushsum(rc,node[p].lz);node[p].lz=0;
		}
		if(node[p].tmx!=-inf)
		{
			pushmax(lc,node[p].tmx);
			pushmax(rc,node[p].tmx);node[p].tmx=-inf;
		}
		if(node[p].tmn!=inf)
		{
			pushmin(lc,node[p].tmn);
			pushmin(rc,node[p].tmn);node[p].tmn=inf;
		}
		return;
	}
	void changesum(int l,int r,ljl val,int p)
	{
		if(l<=node[p].l&&node[p].r<=r)return pushsum(p,val);
		int mid=(node[p].l+node[p].r)/2;
		pushdown(p);
		if(l<=mid)changesum(l,r,val,lc);
		if(mid<r)changesum(l,r,val,rc);
		pushup(p);
		return;
	}
	void changemin(int l,int r,ljl val,int p)
	{
		if(node[p].mx<=val)return;
		if(l<=node[p].l&&node[p].r<=r&&node[p].mx2<val)return pushmin(p,val);
		int mid=(node[p].l+node[p].r)/2;
		pushdown(p);
		if(l<=mid)changemin(l,r,val,lc);
		if(mid<r)changemin(l,r,val,rc);
		pushup(p);
		return;
	}
	void changemax(int l,int r,ljl val,int p)
	{
		if(node[p].mn>=val)return;
		if(l<=node[p].l&&node[p].r<=r&&node[p].mn2>=val)return pushmax(p,val);
		int mid=(node[p].l+node[p].r)/2;
		pushdown(p);
		if(l<=mid)changemax(l,r,val,lc);
		if(mid<r)changemax(l,r,val,rc);
		pushup(p);
		return;
	}
	ljl querysum(int l,int r,int p)
	{
		if(l<=node[p].l&&node[p].r<=r)return node[p].sum;
		pushdown(p);
		int mid=(node[p].l+node[p].r)/2;ljl ans=0;
		if(l<=mid)ans+=querysum(l,r,lc);
		if(mid<r)ans+=querysum(l,r,rc);
		return ans;
	}
	ljl querymin(int l,int r,int p)
	{
		if(l<=node[p].l&&node[p].r<=r)return node[p].mn;
		pushdown(p);
		int mid=(node[p].l+node[p].r)/2;ljl ans=inf;
		if(l<=mid)ans=min(ans,querymin(l,r,lc));
		if(mid<r)ans=min(ans,querymin(l,r,rc));
		return ans;
	}
	ljl querymax(int l,int r,int p)
	{
		if(l<=node[p].l&&node[p].r<=r)return node[p].mx;
		pushdown(p);
		int mid=(node[p].l+node[p].r)/2;ljl ans=-inf;
		if(l<=mid)ans=max(ans,querymax(l,r,lc));
		if(mid<r)ans=max(ans,querymax(l,r,rc));
		return ans;
	}
}
using namespace SMT;
int main(){
	Rd(n);
	FUP(i,1,n)Rd(a[i]);
	bld(1,n,1);
	Rd(m);int op,l,r;ljl x;
	while(m--)
	{
		Rd(op);Rd(l);Rd(r);
		if(1<=op&&op<=3)
		{
			Rd(x);
			if(op==1)changesum(l,r,x,1);
			if(op==2)changemax(l,r,x,1);
			if(op==3)changemin(l,r,x,1);
		}
		else
		{
			if(op==4)printf("%lld\n",querysum(l,r,1));
			if(op==5)printf("%lld\n",querymax(l,r,1));
			if(op==6)printf("%lld\n",querymin(l,r,1));
		}
	}
	return 0;
}
inline void Rd(auto &num)
{
	num=0;char ch=getchar();bool f=0;
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')f=1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		num=(num<<1)+(num<<3)+(ch-'0');
		ch=getchar();
	}
	if(f)num=-num;
	return;
}
相关推荐
闪电悠米1 分钟前
力扣hot100-240.搜索二维矩阵2-单调性剪枝详解
数据结构·算法·leetcode·矩阵·哈希算法
imuliuliang6 分钟前
关于基于图论的最短路径算法性能对比分析7
算法
SilentSlot9 分钟前
【C/C++】手写 DPDK 协议栈(十二):TCP 并发与自实现 epoll 的就绪事件分发
c语言·c++·tcp/ip
雨落在了我的手上13 分钟前
Java数据结构(九):栈和队列
java·开发语言·数据结构
SilentSlot16 分钟前
【C/C++】手写 DPDK 协议栈(六):TCP 三次握手、状态机与数据回显
c语言·c++·tcp/ip
j7~19 分钟前
【C语言】《C语言自定义类型(结构体+联合体+枚举类型)》--详解
c语言·开发语言·深度学习·结构体·位段·联合体·枚举类型
乱七八糟的屋子20 分钟前
AMQP C++ 超详细实战教程(amqp-cpp 开源库从零入门到生产落地)
c++·rabbitmq·任务队列·amqp·流量削峰·c++服务端异步解耦·分布式消息通信
TDengine (老段)27 分钟前
已有TSDB?一条配置,免费解锁AI数据管理平台
大数据·数据库·物联网·ai·时序数据库·tdengine·涛思数据
Escalating_xu30 分钟前
C++11 彻底吃透 emplace_back 与 push_back(最全闭环:左右值|深浅拷贝|移动构造|万能引用|完美转发)
开发语言·c++
imuliuliang31 分钟前
关于从栈与队列看算法思维的演化路径7
算法