UOJ164 - 【清华集训2015】V——单点历史最值

【清华集训2015】V

前言

这个物理背景玩得挺溜。

题解

题面扯了一堆电学转换,最后其实就是裸的叫你输出水的深度。

这道题的意思,简单来讲,就是实现区间加减、区间取 max ⁡ \max max、区间赋值和单点查询当前值、单点查询历史最大值。

这道题非常类似,首先的想法是对线段树上每个区间维护最大值和历史最大值,懒标记记录区间加、区间取最大值和区间赋值操作。三个操作的先后顺序是加减、取 max ⁡ \max max 然后赋值,因为加减操作可以合并到加减和取 max ⁡ \max max 中,而这两者都可以合并到赋值后面。

然而如果直接这么做会导致WA,比如说一个 + 10 +10 +10 的懒标记,还未下传到子节点时又叠上了一层标记变为 + 5 +5 +5 ,这时子节点就不知道自己曾今到达过 a + 10 a+10 a+10,历史最大值就可能算错。这个问题的核心在于懒标记不会及时下传,多个标记的信息的合并对维护当前值是正确的,但对维护历史最大值就假了。

所以我们需要通过记录懒标记合并过程中的“历史最大”懒标记,来解决合并时丢失的历史。具体地说,我们需要维护三种、6个懒标记:

ad:区间加
had:历史最大区间加
mx:区间取max
hm:历史最大区间取max
fz:区间赋值
hfz:历史最大区间赋值

下面是把三种懒标记覆盖到节点上(合并到节点的懒标记中)的部分代码:

inline void cad(int x,ll d,ll hd){  //区间加、历史最大区间加覆盖
	if(fz[x]>-INF)        //如果有赋值标记
		hfz[x]=max(hfz[x],fz[x]+hd),       //更新历史最大赋值
		fz[x]+=d;         //合并到赋值标记
	else{           //没有赋值标记才能越过更新其它标记
		if(mx[x]>-INF)			//如果有取max标记
			hm[x]=max(hm[x],mx[x]+hd),		//更新历史最大取max
			mx[x]+=d;			//合并到取max
		had[x]=max(had[x],ad[x]+hd),	//同时要更新区间加的标记
		ad[x]+=d;
	}
}
inline void cmx(int x,ll m,ll hx){   //区间取max、历史最大取max覆盖
	if(fz[x]>-INF)		//如果有赋值标记
		hfz[x]=max(hfz[x],hx),		//···
		fz[x]=max(fz[x],m);
	else hm[x]=max(hm[x],hx),mx[x]=max(mx[x],m);
}
inline void cfz(int x,ll f,ll hf){   //区间赋值、历史最大区间赋值覆盖
	fz[x]=f,hfz[x]=max(hfz[x],hf);	//只更新赋值标记
}

为什么只改了懒标记?维护的当前值和最值呢?这里用到了一种只适用于区间修改、单点查询的技巧:“去核”。

我们默认所有点初值为0,只存下懒标记,但是最底层的懒标记不清空,然后就可以通过访问最底层懒标记的信息来得到单点的值。区间的最值不维护,因为根本不需要得到它,我们维护的所有东西都只为单点查询服务。

对于这道题来说,我们只需要在建树时把底层节点的“区间赋值”的懒标记设为数组里的初值,单点查询时访问底层节点的“fz”、“hfz”懒标记即可。

少两个数组、少打几段代码、少一点常数,这个技巧还是有点用。

代码

#include<cstdio>//JZM yyds!!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define uns unsigned
#define MAXN 500003
#define INF 1e17
#define lowbit(x) ((x)&(-(x)))
#define IF it->first
#define IS it->second
#define fucking 
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int n,M;
ll a[MAXN];
ll fz[MAXN<<2],hfz[MAXN<<2],ad[MAXN<<2],had[MAXN<<2];
ll mx[MAXN<<2],hm[MAXN<<2];
inline void cad(int x,ll d,ll hd){
	if(fz[x]>-INF)hfz[x]=max(hfz[x],fz[x]+hd),fz[x]+=d;
	else{
		if(mx[x]>-INF)hm[x]=max(hm[x],mx[x]+hd),mx[x]+=d;
		had[x]=max(had[x],ad[x]+hd),ad[x]+=d;
	}
}
inline void cmx(int x,ll m,ll hx){
	if(fz[x]>-INF)hfz[x]=max(hfz[x],hx),fz[x]=max(fz[x],m);
	else hm[x]=max(hm[x],hx),mx[x]=max(mx[x],m);
}
inline void cfz(int x,ll f,ll hf){
	fz[x]=f,hfz[x]=max(hfz[x],hf);
}
inline void fucking PD(int x,int l,int r){
	if(l==r)return;
	if(ad[x]!=0){
		cad(x<<1,ad[x],had[x]),cad(x<<1|1,ad[x],had[x]);
		ad[x]=had[x]=0;
	}
	if(mx[x]>-INF){
		cmx(x<<1,mx[x],hm[x]),cmx(x<<1|1,mx[x],hm[x]);
		mx[x]=hm[x]=-INF;
	}
	if(fz[x]>-INF){
		cfz(x<<1,fz[x],hfz[x]),cfz(x<<1|1,fz[x],hfz[x]);
		fz[x]=hfz[x]=-INF;
	}
}
inline void fucking PU(int x){
	//Nothing
}
inline void fucking build(int x,int l,int r){
	if(l==r){
		fz[x]=hfz[x]=a[l],ad[x]=had[x]=0,mx[x]=hm[x]=-INF;
		return;
	}int mid=(l+r)>>1;
	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
	hm[x]=mx[x]=fz[x]=hfz[x]=-INF,ad[x]=had[x]=0;
}
inline void chg(int x,int l,int r,int a,int b,ll d){
	PD(x,l,r);
	if(l==a&&r==b){cfz(x,d,d);return;}
	int mid=(l+r)>>1;
	if(a<=mid)chg(x<<1,l,mid,a,min(mid,b),d);
	if(b>mid)chg(x<<1|1,mid+1,r,max(a,mid+1),b,d);
	PU(x);
}
inline void add(int x,int l,int r,int a,int b,ll d){
	PD(x,l,r);
	if(l==a&&r==b){cad(x,d,d);return;}
	int mid=(l+r)>>1;
	if(a<=mid)add(x<<1,l,mid,a,min(mid,b),d);
	if(b>mid)add(x<<1|1,mid+1,r,max(a,mid+1),b,d);
	PU(x);
}
inline void cmax(int x,int l,int r,int a,int b,ll d){
	PD(x,l,r);
	if(l==a&&r==b){cmx(x,d,d);return;}
	int mid=(l+r)>>1;
	if(a<=mid)cmax(x<<1,l,mid,a,min(mid,b),d);
	if(b>mid)cmax(x<<1|1,mid+1,r,max(a,mid+1),b,d);
	PU(x);
}
inline ll schfz(int x,int l,int r,int z){
	PD(x,l,r);
	if(l==r)return fz[x];
	int mid=(l+r)>>1;
	if(z<=mid)return schfz(x<<1,l,mid,z);
	else return schfz(x<<1|1,mid+1,r,z);
}
inline ll schhfz(int x,int l,int r,int z){
	PD(x,l,r);
	if(l==r)return hfz[x];
	int mid=(l+r)>>1;
	if(z<=mid)return schhfz(x<<1,l,mid,z);
	else return schhfz(x<<1|1,mid+1,r,z);
}
signed main()
{
	n=read(),M=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,1,n);
	while(M--){
		int op=read();
		if(op==1){
			int l=read(),r=read();
			add(1,1,n,l,r,read());
		}else if(op==2){
			int l=read(),r=read();
			add(1,1,n,l,r,-read()),cmax(1,1,n,l,r,0);
		}else if(op==3){
			int l=read(),r=read();
			chg(1,1,n,l,r,read());
		}else if(op==4)printf("%lld\n",schfz(1,1,n,read()));
		else printf("%lld\n",schhfz(1,1,n,read()));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值