一开始我的想法是,即 f[i]=f[j]+val(j+1,i) f[i]表示到i的最大值,val(l,r)表示l,r之间的最小h,但是是tle,然后呢我就看了大佬的思路(大佬博客),讲到单调栈和线段树,分开来我都能明白,但是组合在一起就有点弄不明白为什么这么搞,然后经过我的幸苦琢磨,终于想的差不多了,以下是我的思路。
我先举一个例子
4
1 4 5 3
-3 4 2 1
先建立一个单调栈c,假设i遍历到1,也就是楼高1的位置,c[1]=0,c[2]=1,c里存的是楼房序号。然后存入线段树
tree[8]=-3;
接着取0到i-1的最大值,存入i。也就是f[i]的值出来了,然后就单点更新tree[9]=f[1]。
然后i=2,c[3]=4。
tree[9]+=4,然后线段树往上更新,
然后重点来了,单调栈的巧妙之处就是c后面的楼高一定高于前面的,所以如果1,2号楼放一起的话,tree[8]的值往上更新,如果分开放的话,就是tree[9]的值往上更新了。
同理i=3
可以看出tree[8]是以c[2]为头头(头头就是这张相片里最矮的那栋楼),后面的楼可以进来的相片,tree[9]是c[2]单独一张照片,c[3]是后面楼的头头,tree[5]是f[2]加上c[3]变成后面楼的头头的情况,然后更新了f[3]。
来到了i=4,出现了变故,新来的三打乱了单调栈,所以我们要把c中大于h[4]的值全都弹出,形成c={0,1,4}。弹出的楼要把他在树上加上的值删掉,然后b[4]加在tree[6]上,往上更新,得到f[4]。
其中有一个小细节,就是tree[9]和tree[5]之后其实就不起作用了,最大值要么是c[2]当头头,要么是4号楼之前保持原样,4号楼自己当下一张相片的头头,具体为什么可以自己举例子讨论一下。
最后附上大佬的代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
using namespace std;
const int Maxn=3e5+10;
const int Maxm=Maxn<<2;
const int inf=(1ll<<60);
int a[Maxn],b[Maxn];
int f[Maxn],c[Maxn];
int maxv[Maxm],add[Maxm];
int n,ans;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
inline void push_up(int k)
{
maxv[k]=max(maxv[k<<1],maxv[k<<1|1]);
}
inline void upd(int k,int v)
{
add[k]+=v;
maxv[k]+=v;
}
inline void push_down(int k)
{
if(add[k]==0)return;
upd(k<<1,add[k]);
upd(k<<1|1,add[k]);
add[k]=0;
}
void modify_seq(int k,int l,int r,int x,int y,int v)
{
if(x<=l && r<=y)return upd(k,v);
push_down(k);
int mid=(l+r)>>1;
if(x<=mid)modify_seq(k<<1,l,mid,x,y,v);
if(mid<y)modify_seq(k<<1|1,mid+1,r,x,y,v);
push_up(k);
}
void modify(int k,int l,int r,int pos,int v)
{
if(l==r)
{
maxv[k]=v;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)modify(k<<1,l,mid,pos,v);
else modify(k<<1|1,mid+1,r,pos,v);
push_up(k);
}
int query(int k,int l,int r,int x,int y)
{
if(x<=l && r<=y)return maxv[k];
int mid=(l+r)>>1,ret=-inf;
push_down(k);
if(x<=mid)ret=query(k<<1,l,mid,x,y);
if(mid<y)ret=max(ret,query(k<<1|1,mid+1,r,x,y));
return ret;
}
int main()
{
// freopen("in.txt","r",stdin);
n=read();
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=1;i<=n;++i)
b[i]=read();
a[0]=-1;
int cnt=1;
f[1]=0;
for(int i=1;i<=n;++i)
{
while(a[c[cnt]]>=a[i] && cnt)
{
modify_seq(1,0,n,c[cnt-1],c[cnt]-1,-b[c[cnt]]);
--cnt;
}
modify_seq(1,0,n,c[cnt],i-1,b[i]);
f[i]=query(1,0,n,0,i-1);
modify(1,0,n,i,f[i]);
c[++cnt]=i;
}
printf("%lld\n",f[n]);
return 0;
}