「CF1313C Skyscrapers」
题目大意
给出一个长度为 \(N\) 的序列 \(a\) 需要构造出一个长度为 \(N\) 的序列 \(h\) 使得 \(\forall i \in \{1,2,\ldots ,N\} h_i \leq a_i\) 且 \(\forall i \in \{2,3,\ldots ,N-1\}\),\(\nexists \forall j \in \{1,2,\ldots ,i-1\},k \in \{i+1,i+2 \ldots N\},h_j > h_i < h_k\),求出并输出 \(\sum_{i=1}^{N}h_i\) 达到最大值时的 \(h\) 序列.
再简化一下题意,就是需要找出一个序列 \(h\),\(h_i\leq a_i\),且以 \(h\) 中的某一个元素看来,前面和后面的元素都是单调的.
本题在数据范围上分为两个部分,所以就写一下两种不同的做法.
Part 1
分析
\(N\) 的范围不大,只有 \(1000\) 于是很容易想到时间复杂度应该是 \(O(N^2)\) 的,可以想到先枚举最高的位置的点,在向两边计算,可以很容易想到最高位置的 \(h_i=a_i\) 而且当最高位置确定以后,为了使得和最大,所以整一个序列也是确定的,那么就可以想出最开始的 \(O(N^2)\) 的做法了
代码
#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
int N,h[114514];
int top;
long long ans[114514];
int main()
{
	scanf("%d",&N);
	REP(i,1,N)
	{
		scanf("%d",&h[i]);
	}
	int now;
	long long answer=0;
	long long sum;
	REP(i,1,N)//枚举最高的位置
	{
		now=h[i];//记录当前的位置
		sum=h[i];
		DOW(j,i-1,1)//向前扫
		{
			sum+=min(h[j],now);
			now=min(h[j],now);//这里的now因为要保证单调不下降,所以需要一直取min
		}
		now=h[i];//向后扫同理
		REP(j,i+1,N)
		{
			sum+=min(h[j],now);
			now=min(h[j],now);
		}
		if(sum>answer)//记录下最大值,以及最大值时时哪一个位置达到最大值了
		{
			answer=sum;
			top=i;
		}
	}
	//最后只需要将达到最大值时的序列输出就好了
	ans[top]=h[top];
	now=h[top];
	DOW(i,top-1,1)
	{
		ans[i]=min(h[i],now);
		now=min(h[i],now);
	}
	now=h[top];
	REP(i,top+1,N)
	{
		ans[i]=min(h[i],now);
		now=min(h[i],now);
	}
	REP(i,1,N)
	{
		printf("%d ",ans[i]);
	}
	return 0;
}
Part 2
分析
\(N\) 的范围变大了很多达到了 \(500000\) 为了防止题解清一色的都是单调栈,这里是一种不是很常见的做法.
可以发现在第一种方法中如果 \(j \leq i,h_j \leq h_i\) 时 \(1\) 到 \(j\) 的最大值肯定是被计算过的,于是就可以想到用一个数组 \(L\) 维护从 \(1\) 开始带第 \(i\) 个位置时最高处为 \(i\) 切 \(h_1\) 到 \(h_i\) 为单调不下降时 \(h_1\) 到 \(h_i\) 和的最大值,计算这个数组需要分两种情况.
- 第一种情况时 \(a_{i-1} \leq a_i\),这样 \(L_i=L_{i-1}+a_i\).
- 第二种情况就比较特殊了,没法直接从 \(L_{i-1}\) 计算出来,需要找到上一个小于等于 \(a_i\) 的位置 \(j\),那么 \(L_i=(j-i)*a_i+L_j\),即在这之间的大于 \(a_i\) 的位置的高度都是 \(a_i\) 这样就可以保证是单调不下降.
于是就要想办法找到上一个小于 \(a_i\) 的位置,显然不可以用 \(O(N)\) 的做法,那么就可以用一颗线段树维护最小值,在线段树上二分找到上一个位置.
用同样的方法计算出一个数组 \(R\)(即从 \(h_i\) 开始到 \(h_N\) 中单调不上升的和的最大值)与 \(L\) 同理,这样以第 \(i\) 个位置为最高值时总和就变成了 \(L_i+R_i-a_i\) 取出最大值以后只要像方法一一样输出就好了.
代码
#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int MAXN=6e5+7;
int N,M;
int arr[MAXN];
long long L[MAXN],R[MAXN];
int ans[MAXN];
//线段树部分,不多讲
struct SegmentTree
{
	int min;
}sgt[MAXN*4];
#define LSON (now<<1)
#define RSON (now<<1|1)
#define MIDDLE ((left+right)>>1)
#define LEFT LSON,left,MIDDLE
#define RIGHT RSON,MIDDLE+1,right
void PushUp(int now)
{
	sgt[now].min=min(sgt[LSON].min,sgt[RSON].min);
}
void Build(int now=1,int left=1,int right=N)
{
	if(left==right)
	{
		sgt[now].min=arr[left];
		return;
	}
	Build(LEFT);
	Build(RIGHT);
	PushUp(now);
}
int QueryL(int l,int r,int num,int now=1,int left=1,int right=N)//查询下一个小于num的值的位置
{
	if(r<left||right<l)return -1;
	if(sgt[now].min>num)
	{
		return -1;
	}
	if(left==right)
	{
		return left;
	}
	int result=QueryL(l,r,num,LEFT);//因为是下一个,所以优先查询左边
	if(result!=-1)
	{
		return result;
	}
	return QueryL(l,r,num,RIGHT);
}
int QueryR(int l,int r,int num,int now=1,int left=1,int right=N)//查询上一个小于num的值的位置
{
	if(r<left||right<l)return -1;
	if(sgt[now].min>num)
	{
		return -1;
	}
	if(left==right)
	{
		return left;
	}
	int result=QueryR(l,r,num,RIGHT);
	if(result!=-1)
	{
		return result;
	}
	return QueryR(l,r,num,LEFT);
}
#undef LSON
#undef RSON
#undef MIDDLE
#undef LEFT
#undef RIGHT
int main()
{
	scanf("%d",&N);
	REP(i,1,N)
	{
		scanf("%d",&arr[i]);
	}
	Build();
	REP(i,1,N)
	{
		if(arr[i]>=arr[i-1])//计算L时的第一种情况
		{
			L[i]=L[i-1]+1ll*arr[i];
		}
		else
		{
			int top=QueryR(1,i-1,arr[i]);//找到上一个小于的位置
			if(top==-1)L[i]=1ll*i*arr[i];//不存在就直接计算
			else
			L[i]=1ll*(i-top)*arr[i]+L[top];//存在按第二种情况计算
		}
	}
	DOW(i,N,1)//计算R同理
	{
		if(arr[i]>=arr[i+1])
		{
			R[i]=R[i+1]+1ll*arr[i];
		}
		else
		{
			int top=QueryL(i+1,N,arr[i]);
			if(top==-1)R[i]=1ll*(N-i+1)*arr[i];
			else
			R[i]=1ll*(top-i)*arr[i]+R[top];
		}
	}
	int top=1;
	long long answer=0;
	long long sum;
	REP(i,1,N)
	{
		sum=L[i]+R[i]-arr[i];
		if(sum>answer)//找到最大位置
		{
			answer=sum;
			top=i;
		}
	}
	//同样方法输出
	ans[top]=arr[top];
	int now=arr[top];
	DOW(i,top-1,1)
	{
		ans[i]=min(arr[i],now);
		now=min(arr[i],now);
	}
	now=arr[top];
	REP(i,top+1,N)
	{
		ans[i]=min(arr[i],now);
		now=min(arr[i],now);
	}
	REP(i,1,N)
	{
		printf("%d ",ans[i]);
	}
	return 0;
}
「CF1313C Skyscrapers」的更多相关文章
- 前端构建工具之gulp(一)「图片压缩」
		前端构建工具之gulp(一)「图片压缩」 已经很久没有写过博客了,现下终于事情少了,开始写博吧 今天网站要做一些优化:图片压缩,资源合并等 以前一直使用百度的FIS工具,但是FIS还没有提供图片压缩的 ... 
- fir.im Weekly - 如何打造 Github 「爆款」开源项目
		最近 Android 转用 Swift 的传闻甚嚣尘上,Swift 的 Github 主页上已经有了一次 merge>>「Port to Android」,让我们对 Swift 的想象又多 ... 
- 更新日志 - fir.im「高级统计」功能上线
		距离 2016 年到来只剩 10 个日夜,fir.im 也准备了一些新鲜的东西,比如「高级统计」功能和「跳转应用商店」功能,帮助你更好地管理.优化应用,欢迎大家试用反馈:) 新增高级统计功能 这次更新 ... 
- Notepad++ 开启「切分窗口」同时检视、比对两份文件
		Notepad++ 是个相当好用的免费纯文本编辑器,除了内建的功能相当多之外,也支持外挂模块的方式扩充各方面的应用.以前我都用 UltraEdit 跟 Emeditor,后来都改用免费的 Notepa ... 
- 「zigbee - 1」工欲善其事必先利其器 - IAR for 8051 IDE customization
		最近在实验室做一些 Zigbee 相关的事情,然而一直没在博客上记录啥东西,也不像原来在公司有动力在 Confluence wiki 上扯东扯西.直到前些阵子,跑到 feibit 论坛上(国内较大的一 ... 
- 「C语言」文件的概念与简单数据流的读写函数
		写完「C语言」单链表/双向链表的建立/遍历/插入/删除 后,如何将内存中的链表信息及时的保存到文件中,又能够及时的从文件中读取出来进行处理,便需要用到”文件“的相关知识点进行文件的输入.输出. 其实, ... 
- 「C语言」Windows+EclipseCDT下的C语言开发环境准备
		之前写过一篇 「C语言」在Windows平台搭建C语言开发环境的多种方式 ,讨论了如何在Windows下用DEV C++.EclipseCDT.VisualStudio.Sublime Test.Cl ... 
- 如何对抗 WhatsApp「蓝色双勾」-- 3 个方法让你偷偷看讯息
		WhatsApp 强制推出新功能「蓝色双勾 (✔✔)」 ,让对方知道你已经看过讯息.一众用户反应极大,因为以后不能再藉口说未看到讯息而不回覆.究竟以后 WhatsApp 是否真的「更难用」? 幸好还有 ... 
- FileUpload控件「批次上传 / 多档案同时上传」的范例--以「流水号」产生「变量名称」
		原文出處 http://www.dotblogs.com.tw/mis2000lab/archive/2013/08/19/multiple_fileupload_asp_net_20130819. ... 
随机推荐
- Python(一):一行解法参考
			#一行快速排序quick_sort = lambda array: array if len(array) <= 1 else quick_sort([item for item in arra ... 
- github,gitlab的区别
			链接:https://blog.csdn.net/Xiamen_XiaoHong/article/details/83655447 总而言之:gitlab最优 
- Angular 2.0 文本拖拽
			基于Angular7.1和TypeScript实现 Html代码 <div style="padding-left: 0px;"> <div id='conten ... 
- [python] VSCode+Jupyter 安装步骤以及注意事项
			1. 安装Python2. 安装Jupyter, pip install 安装Jupyter(若使用Anaconda,则需要将其添加到环境变量中)3. 将Python的Scripts文件夹添加到系统环 ... 
- 微信小程序遮罩层覆盖input失效
			问题:微信小程序中,我们常使用遮罩层,如点击按钮弹出下拉框.弹框等等.若在遮罩层下存在input.textarea.canvas.camera.map.video等标签时,会出现遮罩层覆盖失效的问题. ... 
- libcurl库的简介(二)
			下面是使用libcurl库实现文件上传的一个实例: void CDataProcess::sendFileToServer(void) { string netIp = strNetUrl + &qu ... 
- 流式计算(三)-Flink Stream 篇一
			原创文章,谢绝任何形式转载,否则追究法律责任! 流的世界,有点乱,群雄逐鹿,流实在太多,看完这个马上又冒出一个,也不知哪个才是真正的牛,据说Flink是位重量级选手,能流计算,还能批处理, 和其他伙 ... 
- Springmvc-crud-05(路径错误)
			错误: 原因:Tomcat8之后的一些高版本,使用restful风格访问然后转发到jsp页面,进行业务操作时会报路径错误 解决方案①:修改jsp页面中的page指令isErrorPage=" ... 
- 图片的onerror 事件解析
			1. 该事件触发条件 文档和图像在加载失败的时候(用户体验会下降.)会触发该事件 2. 解决碎图的办法 利用img的onerror事件和javascript 例: 现有的图片是 successed.p ... 
- iframe多层嵌套时,Jquery获取元素
			在项目中,尤其是后台管理项目,会使用到iframe嵌套的网页,说起iframe,真的是个让人头疼的东西,能避开是最好避开.不然要请随身备好氧气瓶哈(因为管理和调试过程中往往会被气缺氧!!!哈哈哈~~~ ... 
