[CSP-S模拟测试]:施工(DP+单调栈+前缀和)
题目描述
小$Y$家门前有一条街道,街道上顺序排列着$n$幢建筑,其中左起第$i$幢建筑的高度为$h_i$。
小$Y$定义街道的不美观度为所有相邻建筑高度差的绝对值之和乘上常数$c$,为了改善街道环境,政府决定进行施工,施工队会选择一些建筑并提升它们的高度,如果一幢建筑最终高度增加了$t$,则需要花费$t_2$的人力。
小$Y$非常好奇,施工完成后街道的不美观度与施工队花费的人力之和最小为多少。
输入格式
第一行包含两个正整数$n,c$。
接下来一行$n$个正整数,表示每幢建筑的高度。
输出格式
输出一行一个整数表示答案。
样例
样例输入:
4 2
1 3 2 4
样例输出:
6
数据范围与提示
对于$100\%$的数据,$n,h_i,c\leqslant 1,000,000$。
$\bullet Subtask\ 1(11pts):n,h_i\leqslant 300$。
$\bullet Subtask\ 2(19pts):n,h_i\leqslant 1,000$。
$\bullet Subtask\ 3(23pts):n\leqslant 1,000$。
$\bullet Subtask\ 4(22pts):n\leqslant 100,000$。
$\bullet Subtask\ 5(25pts):$无特殊限制。
题解
考场上最后$20$分钟$A$了这道题,所以对于这道题我还是比较清楚的,需要先声名一下,因为我和题解打的不太一样,所以在那天讲这道题的时候可能出现了一些偏差(当初我就看到了题解里有“单调栈”三个字我就以为是一样的),耽误了不少同学的学习时间,可能还会有人怀疑我不是自己打的(确实当时将的有点混乱),在此我向大家表示歉意,并在这篇博客里尽可能详细的说一下我的思路。
首先,我们肯定会想到$DP$,大多数人会想定义$dp[i][j]$表示到了第$i$幢建筑,当前建筑高为$j$的最小代价。
转移非常简单,不再赘述,时间复杂度是$\Theta(n\times {\max(k)}^2)$的,但是可以用数据结构优化成$\Theta(n\times \max(k))$的。
但是我们思考,对于一般的非数学题,时间复杂度最多只能减少一次幂,所以这种方式显然是不行的。
所以我就思考改变$DP$的定义,因为我可以在对后面的点进行决策的时候再改变前面的点的高度,所以我们可以在处理当前点的时候先不改变其高度;因此,我们设$dp[i]$表示到了第$i$幢建筑,当前高度为$h[i]$的最小代价。
现在$DP$式子设完了,我们考虑如何转移,这也是这道题的第一个难点,至于题解中说的填平,让好多人有了误解。
因为我们是要从所有的转移点中取一个最小值转移给当前点,而向它转移的点也有很多转移点转移到向它转移的点(不好意思语文不好),例如下图:

假设我们在考虑给$d$转移时,选择了$b$点,并将$c$点填成红色区域,现在我们考虑转移给$e$点,那么你的的理解可能是填成下图:

而这样显然是不优的,所以你就会存在疑问,但是我们是要枚举$a,b,c,d$四个点,取最小值填平,于是我们就可能会填成下图:

那么这个难点就解决了,我们来考虑如何转移:
列出状态转移方程,假设填平高度为$t$:
$$dp[i]=min(dp[j]+\sum \limits_{k=j+1}^{i}{(t-h_k)}^2+c\times abs(h[j]+h[i]-2\times t)$$
来解释一下转台转移方程,对于前半部分,也就是暴力人工填,而后半部分注意$2\times t$是因为你抬高了,所以两边的绝对值都会减。
这时候你可能会发现$t$要枚举,那你就死了。
我们考虑拆开式子,就变成了:
$$\sum \limits_{k=j+1}^{i}{(t-h_k)}^2=(j-i-1)\times t^2-t\times \sum \limits_{k=j+1}^{i}h_k+\sum \limits_{k=j+1}^{i}{h_k}^2$$
现在你要求出$t$,然后你可能回想,这不就是一个单封函数嘛,三分哇。
不得不承认,的确可以,但是我们为什么不考虑直接用二次函数对称轴的公式$-\frac{b}{2a}$来求呢?
所以我们的代价最小的$t$即为:$\dfrac{2\times c\times t+\sum \limits_{k=j+1}^{i}h_k}{2}$。
因为在求对称轴的时候要除以$2$,而整型自动下取整,所以我们可以先加$0.5$再除以$2$。
需要注意的是,我们需要将求出的这个$t$与区间内高度最大的建筑取$\max$。
我们可以维护两个前缀和,这样时间复杂度就变成了$\Theta(n^2)$了。
我们接着考虑优化,使用单调栈,保证栈内元素单调递减,并对于每个点取栈内比它小的点作为转移点,下面考虑这样做的正确性。
首先,求出来的$t$肯定比当前点更小,因为如果填的比它高一定是费力不讨好的,我们不仅需要承担填高中间部分的代价,还要承受绝对值所带来的代价。
然后,因为$t$还要比区间内的$\max(h)$大,所以如果出现下图中的情况,我们在考虑给$c$转移的时候如果还要考虑$a\sim b$中比$b$小的建筑进行转移的话高度$t$一定是$\geqslant h_b$的,所以我们可以直接选择$a\sim d$中的点向$c$转移,而$b\sim c$中的点同理。

因为对于单调栈,每个点最多只会进栈一次,出栈一次,所以我们就优化成了$\Theta(n)$。
还需要注意的一点是,因为$dp[n]$的含义为到了$n$这个点,高度为$h_n$的最小代价,然而最后一个建筑的高度可以比$h_n$大,所以我们可以考虑将$h_0$设为$+\infty$而$h_{n+1}$设为$+\infty -1$,这样$dp[n+1]$就是答案了。
时间复杂度:$\Theta(n)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
int n,c;
long long h[1000010];
int sta[1000010];
long long dp[1000010];
long long s[2][1000010];
long long ans=1LL<<60;
long long ask(int x,int y,int z)
{
long long len=x-y-1;
long long up=s[0][x-1]-s[0][y]+c;
long long down=s[1][x-1]-s[1][y]+h[x]*c;
if(y)
{
up+=c;
down+=h[y]*c;
}
long long res=1.0*up/2/len+0.5;
res=max(res,h[z]);
res=min(res,h[x]);
res=min(res,h[y]);
res=len*res*res-up*res+down;
return res;
}
int main()
{
scanf("%d%d",&n,&c);
for(int i=1;i<=n;i++)
{
scanf("%lld",&h[i]);
s[0][i]=s[0][i-1]+(h[i]<<1);
s[1][i]=s[1][i-1]+h[i]*h[i];
}
h[0]=(h[n+1]=1<<30)--;
sta[1]=0;sta[0]=1;
while(sta[0]&&h[sta[sta[0]]]<=h[1])
{
dp[1]=min(dp[1],dp[sta[sta[0]-1]]+ask(1,sta[sta[0]-1],sta[sta[0]]));
sta[0]--;
}
sta[++sta[0]]=1;
for(int i=2;i<=n;i++)
{
dp[i]=dp[i-1]+c*abs(h[i]-h[i-1]);
while(sta[0]&&h[sta[sta[0]]]<=h[i])
{
dp[i]=min(dp[i],dp[sta[sta[0]-1]]+ask(i,sta[sta[0]-1],sta[sta[0]]));
sta[0]--;
}
sta[++sta[0]]=i;
}
ans=dp[n];
while(sta[0]&&h[sta[sta[0]]]<=1<<30-1)
{
long long len=n-sta[sta[0]-1];
long long up=s[0][n]-s[0][sta[sta[0]-1]];
long long down=s[1][n]-s[1][sta[sta[0]-1]];
if(sta[sta[0]-1])
{
up+=c;
down+=h[sta[sta[0]-1]]*c;
}
long long res=up/2/len+0.5;
res=max(res,h[sta[sta[0]]]);
res=min(res,h[n+1]);
res=min(res,h[sta[sta[0]-1]]);
res=len*res*res-up*res+down;
ans=min(ans,dp[sta[sta[0]-1]]+res);
sta[0]--;
}
cout<<ans<<endl;
return 0;
}
rp++
[CSP-S模拟测试]:施工(DP+单调栈+前缀和)的更多相关文章
- [CSP-S模拟测试]:Cover(单调栈++单调队列+DP)
题目传送门(内部题126) 输入格式 第一行两个个整数$n,m$表示区间的长度与彩灯的数量. 接下来$m$行,每行三个整数$l_i,r_i,a_i$表示一条彩灯能够覆盖的区间以及它的美观程度. 输出格 ...
- [CSP-S模拟测试]:A(单调栈维护凸包+二分答案)
题目传送门(内部题150) 输入格式 第一行两个整数$N,Q$. 接下来的$N$行,每行两个整数$a_i,b_i$. 接下来的$Q$行,每行一个整数$x$. 输出格式 对于每个询问,输出一行一个整数表 ...
- [CSP-S模拟测试]:array(单调栈)
题目描述 在放完棋子之后,$dirty$又开始了新的游戏. 现在他拥有一个长为$n$的数组$A$,他定义第$i$个位置的分值为$i−k+1$,其中$k$需要满足: 对于任意满足$k\leqslant ...
- csp-s模拟测试50(9.22)「施工(单调栈优化DP)」·「蔬菜(二维莫队???)」·「联盟(树上直径)」
改了两天,终于将T1,T3毒瘤题改完了... T1 施工(单调栈优化DP) 考场上只想到了n*hmaxn*hmaxn的DP,用线段树优化一下变成n*hmaxn*log但显然不是正解 正解是很**的单调 ...
- [luogu]P1169 [ZJOI2007]棋盘制作[DP][单调栈]
[luogu]P1169 [ZJOI]棋盘制作 ——!x^n+y^n=z^n 题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋 ...
- 线段树+单调栈+前缀和--2019icpc南昌网络赛I
线段树+单调栈+前缀和--2019icpc南昌网络赛I Alice has a magic array. She suggests that the value of a interval is eq ...
- 【BZOJ 4709】柠檬 斜率优化dp+单调栈
题意 给$n$个贝壳,可以将贝壳分成若干段,每段选取一个贝壳$s_i$,这一段$s_i$的数目为$num$,可以得到$num^2\times s_i$个柠檬,求最多能得到几个柠檬 可以发现只有在一段中 ...
- poj 2796 Feel Good dp || 单调栈
题目链接 题意 对于一个长度为\(n\)的非负整数数列\(a_1,a_2,-,a_n\),求\(max_{1≤l≤r≤n}f(l,r)\), 其中 \[f(l,r)=min(a_l,a_{l+1},- ...
- bzoj 1233: [Usaco2009Open]干草堆tower【dp+单调栈】
参考:https://www.cnblogs.com/N-C-Derek/archive/2012/07/11/usaco_09_open_tower.html 虽然长得很像斜率优化,但是应该不算-- ...
随机推荐
- 网易新闻实战 --- flask,ORM, Ajax异步删除
项目概述: 包含功能: 前端-- 新闻首页 新闻分类页 新闻详情页 后端-- 后台新闻管理(列表,分页) 新增新闻 修改新闻 删除新闻(AJAX)
- ruby基本语法(1)
一些学习资源 http://www.codecademy.com/zh/courses/ruby-beginner-en-d1Ylq/0/5?curriculum_id=5059f8619189a50 ...
- VScode 常用快捷键 2019
窗口操作 Ctrl + b : 显示/隐藏左侧工作区文件目录 View Appearance show Activity bar : 最左侧工具栏 显示/隐藏 Preferences ...
- Mysql基础篇(笔记)
mysql数据库是由DB跟DBMS跟sql组成 DB 数据库仓库 DBMS 数据库管理系统 SQL 一门通用的数据库语言 数据库启动命令 : 关闭->net stop MySQL || 开启 ...
- NOI Day1T1归程(Kruskal重构树+Dijkstra)
NOI Day1T1归程(Kruskal重构树+Dijkstra) 题目 洛谷题目传送门 题解 其实我不想写......,所以...... 挖个坑......我以后一定会补的 luogu的题解讲的还是 ...
- opencv2——图像上的算术运算4
1.图像算术运算 参数含义: src1:第一张图像 src2:第二张图像 dst:destination,目标图像,需要提前分配空间,可省略 mask:掩膜 scale:缩放比,常量 dtype:数据 ...
- How to exploit the x32 recvmmsg() kernel vulnerability CVE 2014-0038
http://blog.includesecurity.com/2014/03/exploit-CVE-2014-0038-x32-recvmmsg-kernel-vulnerablity.html ...
- JS 数组 学习笔记
什么是数组 数组(array)是按次序排列的一组值.每个值的位置都有编号(从0开始),整个数组用方括号表示.本质上数组属于一种特殊的,由Array构造出来的对象,typeof运算符返回数组的类型是 o ...
- 循环冗余校验(CRC)
冗余码 CRC和海明校验类似,也是有效信息(k位)+校验信息(r位),需要满足N=k+r≤2r-1 生成多项式G(X) 定义:收发双方约定的一个(r+1)位二进制数,发送方利用G(X)对信息多项式做模 ...
- [ES6]react中使用es6语法
前言 不论是React还是React-native,facebook官方都推荐使用ES6的语法,没在项目中使用过的话,突然转换过来会遇到一些问题,如果还没有时间系统的学习下ES6那么注意一些常见的写法 ...