CF1483C Skyline Photo
题目
分析
首先看到这个题很明显可以序列上dp,设置状态很常规,就是前i个位置可以得到的最大收益。
关键是转移,考虑朴素的转移:\(dp[i]=\max\limits_{j=1}^{i}\{dp[j-1]+b[minpos]\}\)
然后考虑优化,这类需要用到区间最值的dp转移可以想到单调栈和单调队列等(还有双指针,笛卡尔树,本质差不多)。
于是考虑单调栈,维护区间最小值的断点。
发现这样维护只需要再求个区间dp值的最小值,于是可以线段树。
分析单调栈的复杂度:考虑最坏有n个断点,每一个断点只会被消去一次,势能为\(O(n)\)级别,复杂度正确。
但是我们发现其实复杂度分析当中单调栈和线段树都没啥关系,而且线段树只有一个求区间最小值的作用(而且可以变成只有在后缀加数,不然还是得线段树而不能前缀和来优化),于是还可以想到直接单调栈,用前缀和维护最小值,复杂度可以做到线性。
代码
线段树做法,实现比较简单,单调栈代码可以看luogu题解。
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define ll long long
const ll N=3e5+5,INF=1e18;
int n,h[N],b[N];
struct SegMentTree{
ll Max,add;
#define Max(x) t[x].Max
#define add(x) t[x].add
}t[N<<2];
inline void Pushup(int x){Max(x)=max(Max(x<<1),Max(x<<1|1));}
inline void Modify(int x,ll add){add(x)+=add,Max(x)+=add;}
inline void PushDown(int x){if(add(x)) Modify(x<<1,add(x)),Modify(x<<1|1,add(x)),add(x)=0;}
void Modify(int x,int l,int r,int ql,int qr,ll add){
if(ql<=l && r<=qr) return Modify(x,add);
PushDown(x);
int mid=l+r>>1;
if(ql<=mid) Modify(x<<1,l,mid,ql,qr,add);
if(mid<qr) Modify(x<<1|1,mid+1,r,ql,qr,add);
Pushup(x);
}
ll Query(int x,int l,int r,int ql,int qr){
if(ql<=l && r<=qr) return Max(x);
PushDown(x);
int mid=l+r>>1;ll res=-INF;
if(ql<=mid) res=max(res,Query(x<<1,l,mid,ql,qr));
if(mid<qr) res=max(res,Query(x<<1|1,mid+1,r,ql,qr));
return res;
}
ll f[N];
int q[N],top;
int main(){
read(n);
for(int i=1;i<=n;i++) read(h[i]);
for(int i=1;i<=n;i++) read(b[i]);
for(int i=1;i<=n;i++){
while(top && h[q[top]]>=h[i]) Modify(1,1,n,q[top-1]+1,q[top],-b[q[top]]),top--;
Modify(1,1,n,q[top]+1,i,b[i]);q[++top]=i;
Modify(1,1,n,i,i,f[i-1]);f[i]=Query(1,1,n,1,i);
}
write(f[n]);
return 0;
}