CF1484E Skyline Photo——线段树&单调栈优化DP

E - Skyline Photo

题目描述

n n n 个建筑排成一排,每个建筑有高度值 h i h_i hi ,美观度 b i b_i bi (可以为负),你需要将建筑划分成若干个区间,每个区间的美观度为区间内最矮的建筑的美观度,求所有区间美观度总和的最大值。

数据范围与提示

1 ≤ n ≤ 3 ⋅ 1 0 5 1\le n\le 3\cdot 10^5 1n3105 , − 1 0 9 ≤ b i ≤ 1 0 9 - 10^9\le b_i\le 10^9 109bi109

保证 h h h 值为 1 1 1~ n n n 的排列。

思路

此题很容易想到一个 n 2 n^2 n2 D P DP DP :设 d p [ i ] dp[i] dp[i] 为前 i i i 个建筑划分后美观度的最大值,

首先,每个建筑可以独立成为一个区间:
d p [ i ] = d p [ i − 1 ] + b i dp[i]=dp[i-1]+b_i dp[i]=dp[i1]+bi

h j ( j < i ) h_j(j<i) hj(j<i) j j j~ i i i 中最小的,那么可以把 j + 1 j+1 j+1~ i i i 都加进 j j j 所在的区间:
d p [ i ] = m a x ( d p [ i ] , d p [ j ] ) dp[i]=max(dp[i],dp[j]) dp[i]=max(dp[i],dp[j])
h i h_i hi j j j~ i i i ( j < i j<i j<i )中最小的,那么可以把 j j j~ i i i 都加进一个区间内,相应地,要与 j j j 之前的断开:
d p [ i ] = m a x ( d p [ i ] , d p [ j − 1 ] + b i ) dp[i]=max(dp[i],dp[j-1]+b_i) dp[i]=max(dp[i],dp[j1]+bi)
观察式子发现可以用线段树优化,再用一个单调栈记录可被访问的 j j j 的范围即可。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#define ll long long
#define MAXN 300005
#define uns unsigned
#define INF 0x7f7f7f7f
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,h[MAXN];
ll b[MAXN],dp[MAXN];
ll f[MAXN*3],t[MAXN*3],p;
stack<int>st;
inline void change(int x,ll d){
	for(f[x+p]=d,x=(p+x)>>1;x>0;x>>=1)
		f[x]=max(f[x<<1],f[x<<1|1]);
}
inline ll sch(int l,int r){
	ll res=-INF;
	for(l=p+l-1,r=p+r+1;l^1^r;l>>=1,r>>=1){
		if(~l&1)res=max(res,f[l^1]);
		if(r&1)res=max(res,f[r^1]);
	}
	return res;
}
inline void change_(int x,ll d){
	for(t[x+p]=d,x=(p+x)>>1;x>0;x>>=1)
		t[x]=max(t[x<<1],t[x<<1|1]);
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)h[i]=read();
	for(int i=1;i<=n;i++)b[i]=read();
	for(int i=1;i<=n;i++)dp[i]=-INF;
	for(p=1;p<n+2;p<<=1);
	dp[1]=b[1],h[0]=MAXN;
	memset(f,-0x7f,sizeof(f));
	memset(t,-0x7f,sizeof(t));
	st.push(1);
	change(1,0),change_(1,dp[1]);
	for(int i=2;i<=n;i++){
		dp[i]=max(dp[i],dp[i-1]+b[i]);
		if(h[i-1]<h[i])dp[i]=max(dp[i],dp[i-1]);
		while(!st.empty()&&h[st.top()]>h[i])
			change_(st.top(),-INF),st.pop();
		dp[i]=max(dp[i],t[1]);
		if(!st.empty())dp[i]=max(dp[i],sch(st.top()+1,i)+b[i]);
		else dp[i]=max(dp[i],sch(1,i)+b[i]);
		st.push(i),change(i,dp[i-1]),change_(i,dp[i]);
	}
	printf("%lld\n",dp[n]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值