CodeForces - 1313C2 Skyscrapers (hard version)(单调栈+dp)

题目链接:点击查看

题目大意:给出 n 块连续的空地可以建造摩天大楼,政府有规定,每块地最高只能建 a[ i ] 的高度,同时每栋大楼需要满足一个规则,即每栋大楼的两侧不允许同时存在比自己高的大楼,输出一种方案,使得总高度之和最大

题目分析:C1题目的数据范围是 1e3 ,直接两层 for 暴力枚举每个位置作为最高点就可以了,没有难度的暴力就不多说了,直接说一下C2题目的 5e5 该如何处理吧

看到 5e5 这个数字,再看到题目给出了 3 秒的时限,第一反应肯定是摒弃 n * n 的算法,从而需要思考能否在O(n),O(nlogn)或O(nsqrt(n))中做出选择,赛后看牛客的群中有大牛用了nsqrt(n)分块的做法做出来了所以提一嘴。。其实比赛的时候队友已经想出该如何用 dp O(n) 维护答案了,设dp1[ i ]为正着到第 i 个位置为最高点的最大高度和,dp2[ i ]为反着到第 i 个位置为最高点的最大高度和,预处理完两个dp数组后,就可以O(n)枚举每个位置作为最高点,并且O(1)获得此时的贡献了,此时确定好最高点的位置剩下的输出就不多说了,现在的问题转换为了如何预处理出两个dp数组,其实不难,以dp1为例,当处理到dp[ i ]时,我们只需要找到一个位置 pos ,这个位置是位置 i 左边第一个比 a[ i ] 小的位置,此时 dp[ i ] = dp[ pos ] + ( i - pos ) * a[ i ] ,怎么解释呢,因为 pos 是位置 i 左侧第一个比 a[ i ] 小的位置,所以区间 ( pos , i ] 内的所有高度一定是大于等于 a[ i ] 的,因为我们的dp数组是求以点 i 为最高点时的最大高度和,所以点 i 的高度肯定是最大高度了,显然区间 ( pos , i ] 内的所有点都赋值为 a[ i ] 是最优的,而此时再累加上dp[ pos ]的答案便是当前点 i 的答案了

剩下的问题就变的很简单了,如何求出每个点左边以及右边第一个小于自己的位置呢,单调栈的基础应用,直接套上模板,O(n)维护出位置就可以了,总的时间复杂度为O(n),带着一点常数无伤大雅

代码的话别看着很长,其实复用的部分很多,实际的编程复杂度充其量也只有一半多一点吧,剩下的都是CV大法了

代码:
 

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
      
typedef long long LL;
     
typedef unsigned long long ull;
      
const int inf=0x3f3f3f3f;
 
const int N=5e5+100;

LL a[N],dp1[N],dp2[N];

int l[N],r[N];
 
int main()
{
#ifndef ONLINE_JUDGE
//    freopen("input.txt","r",stdin);
//    freopen("output.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",a+i);
	a[0]=a[n+1]=-inf;
	stack<int>st;
	st.push(0);
	for(int i=1;i<=n;i++)
	{
		while(st.size()&&a[st.top()]>=a[i])
			st.pop();
		l[i]=st.top();
		st.push(i);
	}
	while(st.size())
		st.pop();
	st.push(n+1);
	for(int i=n;i>=1;i--)
	{
		while(st.size()&&a[st.top()]>=a[i])
			st.pop();
		r[i]=st.top();
		st.push(i);
	}
	for(int i=1;i<=n;i++)
		dp1[i]=dp1[l[i]]+(i-l[i])*a[i];
	for(int i=n;i>=1;i--)
		dp2[i]=dp2[r[i]]+(r[i]-i)*a[i];
	LL mmax=-inf,mark;
	for(int i=1;i<=n;i++)
		if(mmax<dp1[i]+dp2[i]-a[i])
		{
			mmax=dp1[i]+dp2[i]-a[i];
			mark=i;
		}
	deque<LL>ans;
	ans.push_back(a[mark]);
	LL pre=a[mark];
	for(int i=mark-1;i>=1;i--)
	{
		pre=min(pre,a[i]);
		ans.push_front(pre);
	}
	pre=a[mark];
	for(int i=mark+1;i<=n;i++)
	{
		pre=min(pre,a[i]);
		ans.push_back(pre);
	}
	for(int i=0;i<ans.size();i++)
		printf("%d ",ans[i]);
     
     
     
     
     
     
     
     
     
     
     
     
     
    return 0;
}
发布了655 篇原创文章 · 获赞 21 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/104470255
今日推荐