1 条题解
-
0
E. Linear Kingdom Races 详细题解
题目重述
有 条道路(编号 到 ),修复道路 需要花费 (非负整数)。有 个比赛,每个比赛是一个区间 ,如果举办该比赛可以获得收益 。举办比赛的前提是区间内所有道路都被修复。你可以任意选择修复哪些道路(不修复也可),目标是最大化 总收益 − 总修复花费。允许不举办任何比赛,此时利润为 。
数据范围:,。
核心思路
这是一道典型的 DP + 线段树优化 问题。
设 表示只考虑前 条道路(即道路 )时,能获得的最大利润。注意 中道路 必须被修复(因为我们要用最后一个位置强制修复来辅助转移)。但最终答案允许最后一条路不修复,因此答案需要对所有 以及 取最大值。状态转移
考虑 怎么计算。
如果修复了道路 ,那么我们可以选择是否举办一些以 为右端点的比赛。设 表示所有右端点 的比赛集合。为了计算 ,我们可以枚举上一个“修复”的位置 (),其中 已经计算好,且道路 被修复( 表示没有道路)。那么区间 内的道路我们都可以自由决定是否修复,但为了获得这些比赛的收益,我们必须修复它们覆盖的所有道路。更好的表述是:当我们从 转移到 时,我们考虑所有右端点 且左端点 的比赛。这些比赛完全位于 内,因此如果要举办它们,需要修复它们覆盖的所有道路,而修复成本已经在 中通过前缀和扣除。具体地:
设 为修复前 条道路的总费用。
如果我们强制修复了道路 ,并选取了某个 作为上一个被修复的道路( 表示之前没修复过任何路),那么区间 内的道路都可能被修复(实际上我们可以选择只修复其中一部分,但为了举办比赛,所有被覆盖的道路都必须修,这会导致额外费用)。真正计算收益的常见技巧是:定义:令 表示考虑前 条道路,且道路 必须被修复 时的最大利润。
转移时,枚举上一个被修复的位置 (),那么:- 我们修复了所有道路 (因为 修复了, 修复了,中间的不能有缺口?实际上为了简化,我们强制区间 全部修复——这是关键,因为如果某个 在 中没有被修复,那么任何跨越它的比赛都无法举行,但我们可以通过将 设为最后一个修复点来涵盖。)
更常用的状态设计是: 表示考虑前 条道路,并且第 条道路被修复时的最大利润。那么转移时,我们考虑上一个被修复的道路 (),那么区间 内的道路都被修复(因为 和 都修复了,但中间如果有的没修复,其实是不允许的,因为如果有比赛要求修复那些路就不能办。然而我们可以通过调整 来避免中间的缺口。实际上这个 DP 隐含了“所有被修复的道路是连续的若干段”的假设吗?不,比赛不要求所有道路都修复,只要求比赛区间内全部修复。所以中间的缺口可能被允许,但转移时我们要考虑的是:以 结尾的所有比赛,它们可能覆盖到更左的位置,所以我们需要在 处加一个“分隔”。
更精妙的方法是使用扫描线 + 线段树。设 如上定义。我们维护一个数组 ,其中 是修复前 条道路的累计花费。那么对于每个 ,考虑所有以 为右端点的比赛 。这些比赛如果能举办,需要修复 之间的所有道路,也就意味着上一个修复点 必须 (因为如果 ,那么 到 之间的道路会被修复吗?不一定,但如果我们把 视为上一个修复点,那么从 到 必须全部修复才能举办此类比赛。因此对于每个比赛,它对所有 的转移贡献 。
于是转移方程可以写为:
$$dp[i] = \max\left(0,\ \max_{0 \le j < i} \left( dp[j] - (pre[i] - pre[j]) + \sum_{(l, i, p) \text{ with } l > j} p \right) \right) $$其中 ,。
$$dp[i] = \max\left(0,\ \max_{0 \le j < i} \left( (dp[j] + pre[j]) + \left( \sum_{l>j} p - pre[i] \right) \right) \right) $$
整理得:定义 。那么对于固定的 ,我们需要对所有 计算 ,其中求和只考虑右端点为 的比赛且左端点 。如果我们能动态维护这个最大值,就可以用线段树实现。
线段树维护技巧
我们按 从 到 扫描。开始时,线段树在位置 存储 。当处理到 时:
- 对于每个以 为右端点的比赛 ,它对所有 (即左端点 > j)的转移有贡献,相当于对区间 整体加上 。
- 然后整个线段树的最大值就是 ,记为 。
- 则 。注意 可能小于 ,我们可以选择不修复 ,所以 。
- 最后,将 插入到线段树的位置 上(实际上是更新该位置的值,因为之前该位置是 )。
最终答案等于 ,因为最后一条道路可以不修复。
算法步骤
- 读入 ,读入修复费用 ,计算前缀和 。
- 将比赛按右端点分组:
races[r].push_back({l, p})。 - 建立线段树,初始时位置 值为 ,其他位置为 。
- 循环 到 :
- 对于
races[i]中的每个比赛(l, p),在线段树上对区间 加上 。 - 查询线段树全局最大值
best。 - 计算
dp[i] = max(0, best - pre[i])。 - 将位置 的值设置为
dp[i] + pre[i](如果比当前值大,实际上直接覆盖,因为之前是负无穷)。
- 对于
- 答案为 。
时间复杂度
- 对比赛进行分类:。
- 每个比赛执行一次区间加操作:,总 。
- 每个 查询一次最大值和一次点更新:,总 。
- 总复杂度 ,满足 的数据范围。
正确性证明
- 我们维护的 表示:如果上一个修复点是 ,且修复了 ,那么到目前 为止,不包含任何右端点 > i 的比赛时,已经获得的收益基础值。
- 每当加入一个以 为右端点的比赛时,所有 的转移都可以获得这个比赛的收益,因此对 区间加 。
- 线段树维护的是 ,即最优的 对应的值(其中 是合法转移点)。
- 减去 得到 ,因为修复 需要花费 ,而 已扣除了 ,所以 ,正好是修复 的净花费。
- 允许 为负时取 ,表示不修复道路 甚至放弃这个转移。
- 最终答案对所有 取最大,是因为可以不修复最后一条路,所以利润可能出现在任何 或 。
关键点总结
- 状态定义 :强制修复第 条路时,前 条路的最大利润。
- 转化:,利用前缀和统一处理修复费用。
- 动态维护:扫描右端点,用线段树维护区间加(比赛收益)和全局最大值查询。
- 最终答案:取所有 和 的最大值。
这道题是 DP 优化与区间操作的经典结合,线段树在其中起到了关键的加速作用。
>>
- 1
信息
- ID
- 6902
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者