题解:洛谷 P9948 Race
【题目来源】
洛谷:P9948 [USACO20JAN] Race B - 洛谷
【题目描述】
Bessie 正在参加一场 \(K(1\le K\le 10^9)\)米的跑步比赛。她从 \(0\) 米每秒的速度开始比赛。在每一秒中,她可以选择将她的速度增加\(1\) 米每秒,保持速度不变,或者将她的速度减少 \(1\) 米每秒。例如,在第一秒中,她可以将她的速度增加到 \(1\)米每秒,跑 \(1\) 米,或者保持她的速度\(0\)米每秒不变,跑 \(0\) 米。Bessie 的速度不会降低到小于零。
Bessie 始终朝着终点线的方向跑,她想要花费整数秒的时间完成比赛。此外,她不想在终点时跑得太快:在 Bessie 跑完 \(K\)米的时刻,她希望她的速度不超过 \(X(1\le X\le 10^5)\)米每秒。Bessie 想要对于\(N(1\le N\le 1000)\)个不同的\(X\) 值知道她多快可以完成比赛。
【输入】
输入的第一行包含两个整数 \(K\) 和 \(N\)。
以下\(N\) 行每行包含一个整数 \(X\)。
【输出】
输出 \(N\) 行,每行包含一个整数,表示 Bessie 完成比赛时的速度小于或等于 \(X\) 的情况下跑完 \(K\)米需要的最小时间。
【输入样例】
10 5
1
2
3
4
5
【输出样例】
6
5
5
4
4
【核心思想】
-
问题分析:给定总路程 \(K\)(\(1 \le K \le 10^9\)),起点速度0,每秒速度可变化 \(-1,0,+1\),且速度非负。要求在跑完 \(K\) 米的结束时刻速度不超过给定的 \(X\),求最小整数秒数。对于 \(N\) 个不同的 \(X\) 分别作答(\(N \le 1000\))。本质是一个带终点速度约束的加速-减速最优控制问题,需要最小化时间,等价于在速度限制下最大化每秒平均速度,且速度曲线呈“先加速后减速”的对称结构。
-
算法选择:
- 数学推导 + 二分答案:对于给定的 \(X\),最优策略是先以 \(+1\) 加速到某个峰值 \(V\),然后以 \(-1\) 减速到终点速度恰好为 \(X\)(或 \(X+1\),根据奇偶性调整)。跑过的总路程是速度曲线下的面积。可以通过二分搜索“峰值速度以上额外运行的时间”来求解。
- 分段计算:将过程分为三段:
- 从0加速到 \(X\)(速度从0到 \(X\),耗时 \(X\) 秒,路程 \(\frac{X(X+1)}{2}\))。
- 以速度 \(X\) 为基底,额外增加一段“等腰梯形”或“三角形”的变速过程(速度从 \(X\) 上升到峰值再回到 \(X\)),该过程增加的路程为 \(m \cdot X + \text{额外面积}\),其中 \(m\) 是额外的时间秒数。
- 从 \(X\) 减速到 \(X\)(实际是回到 \(X\),形成闭合)后的路程计算。
-
关键步骤(基于代码逻辑):
- 预处理:读入 \(K\) 和 \(N\),对每个询问 \(X\) 单独计算。
- 情况1:仅加速段即可完成:计算从0加速到 \(X\) 的路程 \(base = \frac{X(X+1)}{2}\)。若 \(base \ge K\),则只需加速(可能未达到 \(X\) 就已跑完),最小时间 \(t = \lceil \sqrt{2K + 0.25} - 0.5 \rceil\),即满足 \(\frac{v(v+1)}{2} \ge K\) 的最小整数 \(v\)。
- 情况2:需要加速到峰值再减速:
- 令 \(remain = K - base\) 为需要额外跑的路程。
- 二分搜索额外的“往返时间” \(m\)(即在速度 \(X\) 之上运行的总秒数)。\(m\) 的最小可能值为0,最大可能值为 \((K - base) / X\)(取上整)。
- 对于给定的 \(m\),计算在 \(m\) 秒内,速度从 \(X\) 出发,经过加速再减速回到 \(X\) 能提供的最大额外路程 \(extraMax\)。最优结构是:若 \(m\) 为偶数 \(2a\),则先加速 \(a\) 秒再减速 \(a\) 秒,额外路程为 \(a(a+1)\);若 \(m\) 为奇数 \(2a+1\),则先加速 \(a\) 秒,保持峰值 \(1\) 秒,再减速 \(a\) 秒,额外路程为 \((a+1)^2\)。
- 二分找到最小的 \(m\),使得 \(extraMax \ge remain\)。
- 总时间 \(ans = X + m\)(因为 \(base\) 部分耗时 \(X\) 秒,\(m\) 为额外秒数)。
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log K)\),其中 \(N \le 1000\),二分范围约 \(K/X\),但 \(K \le 10^9\),\(\log K\) 约 30,完全可行。
- 空间复杂度:\(O(1)\)。
-
贪心 + 二分最优速度曲线的核心思想:
- 最优策略的对称性:在终点速度限制下,为了最短时间完成固定路程,速度曲线应尽可能“高”且“平坦”。因此,最优方案是单调加速到某个峰值,再单调减速到终点速度 \(X\)(速度变化率为 ±1)。这形成了一个关于峰值对称的梯形(或三角形)。
- 面积分解:将总路程分解为“基础加速段”(0→X)和“额外振荡段”(X→峰值→X)。基础段的时间和路程固定,只需最小化额外段的时间,这转化为给定额外时间 \(m\) 求最大额外路程的单调函数,从而可二分。
- 整数约束处理:速度和路程均为整数,二分边界和公式计算需小心取整。
- 适用场景:适用于一维运动学优化,加速度受限(仅 ±1)且终点速度有上界,求最短时间。核心是利用速度-时间图的面积(路程)进行数学推导和二分。
【解题思路】

【算法标签】
普及 #整数二分
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int k, n, x;
// ifstream filein("race.in");
// ofstream fileout("race.out");
int main()
{
// filein >> k >> n; // 输入k和n
cin >> k >> n; // 输入k和n
while (n--) { // 遍历n次询问
// filein >> x; // 输入x
cin >> x; // 输入x
long long presum = (long long)(x+1)*x/2; // 求出presum(细节处理,需要加上long long)
if (presum>=k) { // 如果presum已经超过了k
// fileout << ceil(sqrt(2*k+0.25)-0.5) << endl; // 反向求
cout << ceil(sqrt(2*k+0.25)-0.5) << endl; // 反向求
continue;
}
int l=0, r=(k-presum)/x; // 二分左值为0,右值为(k-presum)/x
if ((k-presum)%x!=0) { // 如果r不为整数,自增1(细节处理)
r++;
}
while (l<r-1) { // 二分查找
int m = (l+r)/2; // 中间点
int tot = k-presum-x*m; // tot为增加面积。(tot+presum+m*x)>= k
long long cmax=0; // 定义cmax,后面需要与tot比较
int up=m/2; // up取m的一半
if (m%2==0) {
cmax = (long long)up*up; // 增加的面积
} else {
cmax = (long long)(1+up)*up; // 增加的面积(细节处理)
}
if (cmax>=tot) { // 如果cmax大于等于tot,说明m设大了
r = m;
} else { // 否则说明设小了
l = m;
}
}
// fileout << x+r << endl; // x是presum的时间,r为m这段的时间
cout << x+r << endl; // x是presum的时间,r为m这段的时间
}
return 0;
}
【运行结果】
10 5
1
6
2
5
3
5
4
4
5
4
浙公网安备 33010602011771号