题目大意

给出一个长度为 \(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)\) 的做法了

代码

  1. #include<bits/stdc++.h>
  2. #define REP(i,first,last) for(int i=first;i<=last;++i)
  3. #define DOW(i,first,last) for(int i=first;i>=last;--i)
  4. using namespace std;
  5. int N,h[114514];
  6. int top;
  7. long long ans[114514];
  8. int main()
  9. {
  10. scanf("%d",&N);
  11. REP(i,1,N)
  12. {
  13. scanf("%d",&h[i]);
  14. }
  15. int now;
  16. long long answer=0;
  17. long long sum;
  18. REP(i,1,N)//枚举最高的位置
  19. {
  20. now=h[i];//记录当前的位置
  21. sum=h[i];
  22. DOW(j,i-1,1)//向前扫
  23. {
  24. sum+=min(h[j],now);
  25. now=min(h[j],now);//这里的now因为要保证单调不下降,所以需要一直取min
  26. }
  27. now=h[i];//向后扫同理
  28. REP(j,i+1,N)
  29. {
  30. sum+=min(h[j],now);
  31. now=min(h[j],now);
  32. }
  33. if(sum>answer)//记录下最大值,以及最大值时时哪一个位置达到最大值了
  34. {
  35. answer=sum;
  36. top=i;
  37. }
  38. }
  39. //最后只需要将达到最大值时的序列输出就好了
  40. ans[top]=h[top];
  41. now=h[top];
  42. DOW(i,top-1,1)
  43. {
  44. ans[i]=min(h[i],now);
  45. now=min(h[i],now);
  46. }
  47. now=h[top];
  48. REP(i,top+1,N)
  49. {
  50. ans[i]=min(h[i],now);
  51. now=min(h[i],now);
  52. }
  53. REP(i,1,N)
  54. {
  55. printf("%d ",ans[i]);
  56. }
  57. return 0;
  58. }

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\) 和的最大值,计算这个数组需要分两种情况.

  1. 第一种情况时 \(a_{i-1} \leq a_i\),这样 \(L_i=L_{i-1}+a_i\).
  2. 第二种情况就比较特殊了,没法直接从 \(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\) 取出最大值以后只要像方法一一样输出就好了.

代码

  1. #include<bits/stdc++.h>
  2. #define REP(i,first,last) for(int i=first;i<=last;++i)
  3. #define DOW(i,first,last) for(int i=first;i>=last;--i)
  4. using namespace std;
  5. const int MAXN=6e5+7;
  6. int N,M;
  7. int arr[MAXN];
  8. long long L[MAXN],R[MAXN];
  9. int ans[MAXN];
  10. //线段树部分,不多讲
  11. struct SegmentTree
  12. {
  13. int min;
  14. }sgt[MAXN*4];
  15. #define LSON (now<<1)
  16. #define RSON (now<<1|1)
  17. #define MIDDLE ((left+right)>>1)
  18. #define LEFT LSON,left,MIDDLE
  19. #define RIGHT RSON,MIDDLE+1,right
  20. void PushUp(int now)
  21. {
  22. sgt[now].min=min(sgt[LSON].min,sgt[RSON].min);
  23. }
  24. void Build(int now=1,int left=1,int right=N)
  25. {
  26. if(left==right)
  27. {
  28. sgt[now].min=arr[left];
  29. return;
  30. }
  31. Build(LEFT);
  32. Build(RIGHT);
  33. PushUp(now);
  34. }
  35. int QueryL(int l,int r,int num,int now=1,int left=1,int right=N)//查询下一个小于num的值的位置
  36. {
  37. if(r<left||right<l)return -1;
  38. if(sgt[now].min>num)
  39. {
  40. return -1;
  41. }
  42. if(left==right)
  43. {
  44. return left;
  45. }
  46. int result=QueryL(l,r,num,LEFT);//因为是下一个,所以优先查询左边
  47. if(result!=-1)
  48. {
  49. return result;
  50. }
  51. return QueryL(l,r,num,RIGHT);
  52. }
  53. int QueryR(int l,int r,int num,int now=1,int left=1,int right=N)//查询上一个小于num的值的位置
  54. {
  55. if(r<left||right<l)return -1;
  56. if(sgt[now].min>num)
  57. {
  58. return -1;
  59. }
  60. if(left==right)
  61. {
  62. return left;
  63. }
  64. int result=QueryR(l,r,num,RIGHT);
  65. if(result!=-1)
  66. {
  67. return result;
  68. }
  69. return QueryR(l,r,num,LEFT);
  70. }
  71. #undef LSON
  72. #undef RSON
  73. #undef MIDDLE
  74. #undef LEFT
  75. #undef RIGHT
  76. int main()
  77. {
  78. scanf("%d",&N);
  79. REP(i,1,N)
  80. {
  81. scanf("%d",&arr[i]);
  82. }
  83. Build();
  84. REP(i,1,N)
  85. {
  86. if(arr[i]>=arr[i-1])//计算L时的第一种情况
  87. {
  88. L[i]=L[i-1]+1ll*arr[i];
  89. }
  90. else
  91. {
  92. int top=QueryR(1,i-1,arr[i]);//找到上一个小于的位置
  93. if(top==-1)L[i]=1ll*i*arr[i];//不存在就直接计算
  94. else
  95. L[i]=1ll*(i-top)*arr[i]+L[top];//存在按第二种情况计算
  96. }
  97. }
  98. DOW(i,N,1)//计算R同理
  99. {
  100. if(arr[i]>=arr[i+1])
  101. {
  102. R[i]=R[i+1]+1ll*arr[i];
  103. }
  104. else
  105. {
  106. int top=QueryL(i+1,N,arr[i]);
  107. if(top==-1)R[i]=1ll*(N-i+1)*arr[i];
  108. else
  109. R[i]=1ll*(top-i)*arr[i]+R[top];
  110. }
  111. }
  112. int top=1;
  113. long long answer=0;
  114. long long sum;
  115. REP(i,1,N)
  116. {
  117. sum=L[i]+R[i]-arr[i];
  118. if(sum>answer)//找到最大位置
  119. {
  120. answer=sum;
  121. top=i;
  122. }
  123. }
  124. //同样方法输出
  125. ans[top]=arr[top];
  126. int now=arr[top];
  127. DOW(i,top-1,1)
  128. {
  129. ans[i]=min(arr[i],now);
  130. now=min(arr[i],now);
  131. }
  132. now=arr[top];
  133. REP(i,top+1,N)
  134. {
  135. ans[i]=min(arr[i],now);
  136. now=min(arr[i],now);
  137. }
  138. REP(i,1,N)
  139. {
  140. printf("%d ",ans[i]);
  141. }
  142. return 0;
  143. }

「CF1313C Skyscrapers」的更多相关文章

  1. 前端构建工具之gulp(一)「图片压缩」

    前端构建工具之gulp(一)「图片压缩」 已经很久没有写过博客了,现下终于事情少了,开始写博吧 今天网站要做一些优化:图片压缩,资源合并等 以前一直使用百度的FIS工具,但是FIS还没有提供图片压缩的 ...

  2. fir.im Weekly - 如何打造 Github 「爆款」开源项目

    最近 Android 转用 Swift 的传闻甚嚣尘上,Swift 的 Github 主页上已经有了一次 merge>>「Port to Android」,让我们对 Swift 的想象又多 ...

  3. 更新日志 - fir.im「高级统计」功能上线

    距离 2016 年到来只剩 10 个日夜,fir.im 也准备了一些新鲜的东西,比如「高级统计」功能和「跳转应用商店」功能,帮助你更好地管理.优化应用,欢迎大家试用反馈:) 新增高级统计功能 这次更新 ...

  4. Notepad++ 开启「切分窗口」同时检视、比对两份文件

    Notepad++ 是个相当好用的免费纯文本编辑器,除了内建的功能相当多之外,也支持外挂模块的方式扩充各方面的应用.以前我都用 UltraEdit 跟 Emeditor,后来都改用免费的 Notepa ...

  5. 「zigbee - 1」工欲善其事必先利其器 - IAR for 8051 IDE customization

    最近在实验室做一些 Zigbee 相关的事情,然而一直没在博客上记录啥东西,也不像原来在公司有动力在 Confluence wiki 上扯东扯西.直到前些阵子,跑到 feibit 论坛上(国内较大的一 ...

  6. 「C语言」文件的概念与简单数据流的读写函数

    写完「C语言」单链表/双向链表的建立/遍历/插入/删除 后,如何将内存中的链表信息及时的保存到文件中,又能够及时的从文件中读取出来进行处理,便需要用到”文件“的相关知识点进行文件的输入.输出. 其实, ...

  7. 「C语言」Windows+EclipseCDT下的C语言开发环境准备

    之前写过一篇 「C语言」在Windows平台搭建C语言开发环境的多种方式 ,讨论了如何在Windows下用DEV C++.EclipseCDT.VisualStudio.Sublime Test.Cl ...

  8. 如何对抗 WhatsApp「蓝色双勾」-- 3 个方法让你偷偷看讯息

    WhatsApp 强制推出新功能「蓝色双勾 (✔✔)」 ,让对方知道你已经看过讯息.一众用户反应极大,因为以后不能再藉口说未看到讯息而不回覆.究竟以后 WhatsApp 是否真的「更难用」? 幸好还有 ...

  9. FileUpload控件「批次上传 / 多档案同时上传」的范例--以「流水号」产生「变量名称」

    原文出處  http://www.dotblogs.com.tw/mis2000lab/archive/2013/08/19/multiple_fileupload_asp_net_20130819. ...

随机推荐

  1. spring cloud config 连接GitHub访问 报错 Cannot clone or checkout repository

    原因是建立仓库的时候将仓库私有化了,将仓库公有 或者 设置账号密码即可!

  2. 一起了解 .Net Foundation 项目 No.3

    .Net 基金会中包含有很多优秀的项目,今天就和笔者一起了解一下其中的一些优秀作品吧. 中文介绍 中文介绍内容翻译自英文介绍,主要采用意译.如与原文存在出入,请以原文为准. AutoMapper Au ...

  3. 面试题17.打印从1到最大的n位数

    void print_n_number(int n){ if(n<=0){ cout<<"fuckyou"; return; } string s="1 ...

  4. Java连载64-finally语法及其注意事项

    一.finally语句块 1.注意点: (1)finally语句块可以直接和try语句块联合使用.try...finally.... (2)try.....catch.....finally也可以执行 ...

  5. 短网址(t.cn、url.cn)生成,网址缩短接口API

    简要说明 短网址api接口有很多格式,不同的接口生成的短网址格式也不同,比如常见的t.cn.url.cn.w.url.cn等格式.总而言之短网址接口就是用来将一个冗长的链接缩短成10个字符以内的短链接 ...

  6. drf解析模块,异常模块,响应模块,序列化模块

    复习 """ 1.接口:url+请求参数+响应参数 Postman发送接口请求的工具 method: GET url: https://api.map.baidu.com ...

  7. Codeforces Round #618 (Div. 1)C(贪心)

    把所有数看作N块,后面的块比前面的块小的话就合并,这个过程可能会有很多次,因为后面合并后会把前面的块均摊地更小,可能会影响更前面地块,像是多米诺骨牌效应,从后向前推 #define HAVE_STRU ...

  8. 每天进步一点点------Altium Designer集成库简介及创建

    一.集成库概述    Altium Designer 采用了集成库的概念.在集成库中的元件不仅具有原理图中代表元件的符号,还集成了相应的功能模块.如Foot Print 封装,电路仿真模块,信号完整性 ...

  9. 操作word

    package com.gwt.flow.task; import java.io.File; import java.io.FileInputStream; import java.io.FileN ...

  10. LeetCode 编辑距离(DP)

    题目 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 . 你可以对一个单词进行如下三种操作: 插入一个字符 删除一个字符 替换一个字符 思路 定 ...