1 条题解
-
0
题目详细题解
问题重述
给定一个长度为 的数组 ,表示每条跑道的路段数。
对于每个查询 ,需要选择一个 ,使得完成第 到 条跑道总共 个路段后,能力的总增加量最大。能力增加规则:
完成第 段增加 ,第 段增加 ,……,第 段增加 。因此总增加量为:
$$f(k) = \sum_{j=1}^{k} (u+1-j) = k \cdot u - \frac{k(k-1)}{2} $$其中 。
核心观察
-
的性质
$$f(k) = -\frac{1}{2}k^2 + \left(u + \frac{1}{2}\right)k $$
是一个关于 的二次函数:它是开口向下的抛物线,对称轴在 附近。
即:当 时, 随 增加而增加;
当 超过 很多时, 会下降。 -
最优 的位置
由于 先增后减,最优的 应该在 附近。
具体来说:- 如果总路段数 ,那么每一段的增加量都是非负的,继续增加路段一定更好。
- 当 时,后面会出现负增益,但总增加量可能仍然比 时大(例如 , 时最后一段 ,总和不减; 时最后一段 ,但前面仍然有 段正增益)。
-
关键结论(来自标程思路)
对于固定的 和 :- 找到最大的 ,使得总路段数 ,记这个 为 。
- 最优的 只可能在 或 (甚至附近小范围)中产生。
- 更远的 由于 远大于 ,负增益太多,不可能更优。
为什么?因为 在 时开始递减,且递减速度加快。
所以只需要检查 和 两个候选即可。但为了安全(防止边界情况),标程检查了 附近 的范围。
算法步骤
1. 预处理前缀和
设 ,则从 到 的总路段数为:
2. 对于每个查询
第一步:二分查找最大的 使得
lb = l, rb = n; while(lb < rb) { int mid = (lb + rb + 1) >> 1; if(ps[mid] - ps[l-1] <= u) lb = mid; else rb = mid - 1; }这里 就是我们要的 。
第二步:在 附近小范围枚举最优解
检查 :
int t = ps[i] - ps[l-1]; int ut = (u + (u - t + 1)) * t / 2;这里的
$$f(t) = u + (u-1) + \dots + (u-t+1) = \frac{(u + (u-t+1)) \cdot t}{2} $$ut计算了 :这是等差数列求和公式。
第三步:选择 最大的 ,若相等选最小的
由于我们按 从小到大枚举,当
ut > maxu时才更新,自然保证了相等时取较小下标。
为什么只检查附近几个点?
因为 是单峰函数(先增后减),峰值在 或 附近。
而 只能取前缀和的值,这些值不是连续的,但 对应最大的 , 对应最小的 。
最优解一定在这两个 对应的 中。
再往外, 只会更小。检查 是为了处理边界情况(例如 很大导致 跳过 较远)。
复杂度分析
- 前缀和:
- 每个查询:二分 + 常数次检查
- 总复杂度:,满足题目要求。
示例验证(第一个查询)
- 二分找到最大的 使 :()
- 检查 :
- :
- :
- :
- 最大值为 ,取最小 →
输出 ,与题意一致。
总结
这道题的核心是:
- 将问题转化为二次函数求最值。
- 利用前缀和快速得到总路段数。
- 二分找到 的最大 。
- 只在 附近检查几个候选点,因为单峰函数的最值就在峰顶附近。
标程的实现非常简洁高效,是典型的 二分 + 数学 题目。
-
- 1
信息
- ID
- 6506
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者