题目描述
有一条长度为 LLL 的无摩擦跑道 ABABAB ,两端分别是 AAA 和 BBB 。两名运动员分别从两端出发:
- 运动员 111 从 AAA 出发,速度恒为 uuu 。
- 运动员 222 从 BBB 出发,速度恒为 vvv 。
两人到达另一端时立即掉头(不计掉头时间),并持续无限比赛。在比赛过程中,他们会在途中相遇(迎面相遇或超越时),也可能在端点相遇。给定比赛时间 ttt ,求在时间 ttt 内他们相遇的总次数(包括途中相遇和端点相遇)。
输入格式
每个测试用例包含四个 323232 位有符号非负整数:
LLL uuu vvv ttt
其中 L≠0L \neq 0L=0 。输入以一行 0 0 0 0 结束(不处理)。输入文件共有 100181001810018 行数据。
输出格式
对每个测试用例,输出相遇次数。
样例输入
10 5 5 4
10 5 10 4
10 10 10 4
0 0 0 0
样例输出
2
3
4
题目分析
问题本质
这是一个典型的相遇问题,但有以下特殊之处:
- 无限往返运动:运动员到达端点后立即掉头继续运动。
- 多种相遇方式:包括迎面相遇、同向超越、端点相遇。
- 无限时间内的有限时间统计:需要计算在给定时间 ttt 内的总相遇次数。
关键观察
- 相对运动视角:将两个运动员的运动分解为相对运动。
- 两种相遇模式:
- 迎面相遇:当两人相向而行时相遇。
- 同向超越:当速度较快的运动员追上速度较慢的运动员时。
- 端点相遇的特殊性:在端点相遇同时满足两种相遇模式的条件,需要特殊处理。
数学模型推导
1. 迎面相遇的条件
设第 kkk 次迎面相遇的时间为 tkt_ktk ,则有:
(u+v)⋅tk=(2k+1)⋅L
(u + v) \cdot t_k = (2k + 1) \cdot L
(u+v)⋅tk=(2k+1)⋅L
其中 k=0,1,2,…k = 0, 1, 2, \ldotsk=0,1,2,…
推导:第一次迎面相遇发生在相对运动距离为 LLL 时,之后每次迎面相遇需要相对运动 2L2L2L 的距离。
在时间 ttt 内,满足条件的 kkk 的最大值为:
kmax=⌊(u+v)⋅t−L2L⌋
k_{\max} = \left\lfloor \frac{(u+v) \cdot t - L}{2L} \right\rfloor
kmax=⌊2L(u+v)⋅t−L⌋
但更好的表达方式是直接计算次数:
迎面相遇次数=⌊(u+v)⋅t+L2L⌋
\text{迎面相遇次数} = \left\lfloor \frac{(u+v) \cdot t + L}{2L} \right\rfloor
迎面相遇次数=⌊2L(u+v)⋅t+L⌋
2. 同向超越的条件
假设 u≥vu \ge vu≥v (速度较大者在前或在后都可能发生超越)。设第 kkk 次同向超越的时间为 sks_ksk ,则有:
(u−v)⋅sk=(2k+1)⋅L
(u - v) \cdot s_k = (2k + 1) \cdot L
(u−v)⋅sk=(2k+1)⋅L
其中 k=0,1,2,…k = 0, 1, 2, \ldotsk=0,1,2,…
在时间 ttt 内,同向超越次数为:
同向超越次数=⌊(u−v)⋅t+L2L⌋
\text{同向超越次数} = \left\lfloor \frac{(u-v) \cdot t + L}{2L} \right\rfloor
同向超越次数=⌊2L(u−v)⋅t+L⌋
3. 重复计算的端点相遇
当两个运动员在端点相遇时,这个时间点同时满足:
(u+v)⋅t=(2k1+1)⋅L和(u−v)⋅t=(2k2+1)⋅L
(u+v) \cdot t = (2k_1+1) \cdot L \quad \text{和} \quad (u-v) \cdot t = (2k_2+1) \cdot L
(u+v)⋅t=(2k1+1)⋅L和(u−v)⋅t=(2k2+1)⋅L
这意味着端点相遇被计算了两次(一次作为迎面相遇,一次作为同向超越),需要减去一次。
端点相遇发生的条件是:
u−vgcd(u,v) 是奇数
\frac{u-v}{\gcd(u,v)} \text{ 是奇数}
gcd(u,v)u−v 是奇数
此时需要减去的次数为:
⌊gcd(u,v)⋅t+L2L⌋
\left\lfloor \frac{\gcd(u,v) \cdot t + L}{2L} \right\rfloor
⌊2Lgcd(u,v)⋅t+L⌋
4. 特殊情况处理
- 当 u=0u = 0u=0 且 v=0v = 0v=0 时,两个运动员都不动,永远不会相遇(除了初始时分别在两端,不算相遇),答案为 000 。
- 当 u=vu = vu=v 时,没有同向超越,只有迎面相遇。此时 (u−v)=0(u-v) = 0(u−v)=0 ,公式依然适用。
- 当 u<vu < vu<v 时,交换 uuu 和 vvv ,保证 u≥vu \ge vu≥v ,公式依然成立。
最终公式
总相遇次数为:
ans=⌊(u+v)⋅t+L2L⌋+⌊(u−v)⋅t+L2L⌋−重复部分
\texttt{ans} = \left\lfloor \frac{(u+v) \cdot t + L}{2L} \right\rfloor + \left\lfloor \frac{(u-v) \cdot t + L}{2L} \right\rfloor - \text{重复部分}
ans=⌊2L(u+v)⋅t+L⌋+⌊2L(u−v)⋅t+L⌋−重复部分
其中重复部分为:
重复部分={⌊gcd(u,v)⋅t+L2L⌋如果 u−vgcd(u,v) 是奇数0否则
\text{重复部分} =
\begin{cases}
\left\lfloor \frac{\gcd(u,v) \cdot t + L}{2L} \right\rfloor & \text{如果 } \frac{u-v}{\gcd(u,v)} \text{ 是奇数} \\
0 & \text{否则}
\end{cases}
重复部分={⌊2Lgcd(u,v)⋅t+L⌋0如果 gcd(u,v)u−v 是奇数否则
解题思路
算法步骤
- 读入数据:循环读入 L,u,v,tL, u, v, tL,u,v,t ,直到 L=0L = 0L=0 。
- 特判零速度:如果 u=0u = 0u=0 且 v=0v = 0v=0 ,输出 000 ,继续下一组数据。
- 确保 u≥vu \ge vu≥v:如果 u<vu < vu<v ,交换两者的值。
- 计算迎面相遇次数:
ans=⌊(u+v)⋅t+L2L⌋ \texttt{ans} = \left\lfloor \frac{(u+v) \cdot t + L}{2L} \right\rfloor ans=⌊2L(u+v)⋅t+L⌋ - 加上同向超越次数:
ans+=⌊(u−v)⋅t+L2L⌋ \texttt{ans} += \left\lfloor \frac{(u-v) \cdot t + L}{2L} \right\rfloor ans+=⌊2L(u−v)⋅t+L⌋ - 减去重复计算的端点相遇:
- 计算 d=gcd(u,v)d = \gcd(u, v)d=gcd(u,v)
- 如果 (u−v)/d(u-v)/d(u−v)/d 是奇数,则减去 ⌊d⋅t+L2L⌋\left\lfloor \frac{d \cdot t + L}{2L} \right\rfloor⌊2Ld⋅t+L⌋
- 输出结果
时间复杂度
- 每组数据只需要 O(1)O(1)O(1) 时间计算。
- 主要操作是整数运算和一次 gcd\gcdgcd 计算。
- 总时间复杂度为 O(nlogmin(u,v))O(n \log \min(u,v))O(nlogmin(u,v)) ,其中 nnn 是数据组数。
空间复杂度
- 只需要常数空间存储变量。
注意事项
- 使用 646464 位整数: (u+v)⋅t(u+v) \cdot t(u+v)⋅t 可能超过 323232 位整数范围,需要使用
long long类型。 - 整数除法:C++\texttt{C++}C++ 中的整数除法是向零取整,但公式中的除法是向下取整。由于所有值都是非负整数,直接使用整数除法即可。
- gcd\gcdgcd 计算:可以使用
__gcd函数(C++ 标准库)或自己实现。
参考代码
// Infinite Race
// UVa ID: 10627
// Verdict: Accepted
// Submission Date: 2025-12-14
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
int main() {
long long L, u, v, t;
while (cin >> L >> u >> v >> t) {
if (L == 0) break;
if (u == 0 && v == 0) {
cout << 0 << endl;
continue;
}
// 确保 u >= v
if (u < v) swap(u, v);
long long ans = 0;
// 迎面相遇次数
ans += ((u + v) * t + L) / (2 * L);
// 同向超越次数
ans += ((u - v) * t + L) / (2 * L);
// 减去重复计算的端点相遇
long long d = __gcd(u, v);
if ((u - v) / d % 2 != 0) ans -= (d * t + L) / (2 * L);
cout << ans << endl;
}
return 0;
}
总结
本题是一个经典的相遇问题,但加入了往返运动和端点相遇的复杂情况。解题的关键在于:
- 分离两种相遇模式:迎面相遇和同向超越。
- 数学建模:将相遇时间表示为等差数列的形式。
- 处理重复计算:端点相遇同时满足两种模式的条件,需要减去重复计数。
通过数学推导得到简洁的公式,再结合整数运算实现高效的计算。代码实现简洁明了,时间复杂度低,能够快速处理大量输入数据。

1599

被折叠的 条评论
为什么被折叠?



