[国家集训队2011]拆迁队 解题报告

本文详细解析了如何运用动态规划(DP)和斜率优化技术解决在给定美观度序列的情况下,最大化保留旧房子数量并最小化改造总成本的问题。通过寻找最长不下降子序列和维护凸包,实现高效算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:
lanxisi带领着他的拆迁队来整治一个街道。这个街道由N个旧房子组成,从左到右编号为1..N。每个旧房子i有一个正整数的美观度A i
lanxisi希望整个街道从左到右美观度严格递增,也就是保证A i j(i
现在,请你求出lanxisi最多能保留多少旧房子和整治这个街道所需要的最少总花费(当然是在保留旧房子最多这个前提下的)。

分析:
首先这种题一看要么贪心要么DP,对吧……贪心估计不靠谱, 所以往DP上想。

因为需要尽可能保留旧房子,所以先考虑一个问题:假设我们保留的房子中,有两栋是i,j(i

显然,条件应该是A[i]+j-i<=A[j],因为中间那些房子在重建后的高度至少应该是A[i]+1,...,A[i]+j-i-1(j=i+1并不影响结论)。换句话说,应该满足A[i]-i<=A[j]-j。
那就可以解决“保留最多旧房子”这个问题了:求出数列{A[i]-i}的最长不下降子序列,其长度就是最多能保留的房子数。最长不下降子序列用O(NlogN)的算法求,下面设以i为结尾的最长不下降子序列长度为g[i]。

再来考虑DP。
设f[i]表示:当前进行到i,要求留下i这个房子的最小花费。

然后考虑f[j]由什么转移而来……显然是上一个保留的房子i(j是第一栋保留的房子这种情况只需要特判一下,并不影响我们的讨论)。i应该满足什么条件?首先需要i<=A[j]-j,不过还有另外一个条件:g[i]+1=g[j],否则就无法达到“保留最多旧房子”这个要求了。

然后我们把条件转移方程列出来。
对于决策i,它所得出的f[j]有三个部分:
①1~i的花费,即f[i]
②i+1~j的花费,即(A[i]+1)+(A[i]+2)+...+(A[i]+j-i-1)
③j的花费,即A[j]+B[j]
将它们写出来,就是这样的:
[国家集训队2011]拆迁队 <wbr>解题报告
(图来自出题人的解题报告,下同)

有没有感到一股斜率优化的气息……括弧笑
于是分离变量,结果是这样的:
[国家集训队2011]拆迁队 <wbr>解题报告

分成三个部分:第一个括号只和i有关,第二个括号是由i确定的一个值*j,第三个括号只和j有关。
然后现在假设……我们得出了所有能转移到j的i,然后如何用斜率优化呢?把那些i都变成坐标上的点,其横坐标为2A[i]-2i,纵坐标就是前面那个只和i有关的式子,然后用直线束x*j+y=C去从下往上截这些点,第一个碰到的点就是最优决策。

而此直线束的斜率是-j,所以可以想到,碰到的点一定处于下凸壳上。
所以维护下凸壳,然后在凸壳上二分,就可以找到答案。
这里说明一下:对于有些的题目,那个斜率是单调的,所以可以用一个指针单调跑,但这道题就不行,原因是后面可以看到,按照DP的顺序,j并不单调。

那么现在的问题就变成了如何快速找到我们想要的凸包。
再重复一遍i能转移到j的三个条件:i<=A[j],g[i]+1=g[j]。
题解中的做法是:依次处理每一个g值,比如说假设当前想要处理满足g[j]=5的若干个j(下面称之为‘位置’),那它们一定都由g=4的某位置转移而来。

所以将g=4和g=5的位置们都按照A[i]-i的值从小到大排序,然后建立一棵线段树,下标是在原序列中的位置(当然需要离散化)。线段树的每个节点都存放它对应这一段的中,所有g=4的位置所形成的凸包,显然这需要O(NlogN)的空间。

用两个指针分别从小到大地扫描g=4和g=5的诸位置,确保线段树中存放的那些点都能转移给当前的位置。
其中我们可能会进行两种操作:
①向线段树中加入一个g=4的位置,方法就是从根走到叶子,把它对应的点插入到每个路径上的节点中。这是向O(logN)个凸包中各加入了一个点,均摊耗时O(logN)。
②查询一个g=5的位置j,方法就是在线段树上查询能这一段(1~j-1离散化后对应的那一段),对于它覆盖的每个节点,都在其凸包中二分查找一下。这是在O(logN)个凸包中都二分查找,耗时O(log^2N)。

如此从小扫到大,总复杂度显然是O(log^2N)。

但是!既然都把线段树写出来了,就不用按照g从小到大扫了。我们完全可以对每个g值同时建线段树,显然线段树占用空间仍然是O(NlogN)。然后以A[i]-i为第一关键字,g[i]为第二关键字从小到大排序,按照这一顺序进行DP。每次扫描到一个点i时,先在g[i]-1的线段树中进行查询,然后将其插到g[i]的线段树中。

代码:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值