【清华集训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;
}