Description
在一条
[
0
,
�
]
[0,n] 的线段上有两名旅行者
�
,
�
A,B,
�
A 从
�
1
p
1
开始以每秒
�
1
v
1
单位的速度运动,
�
B 从
�
2
p
2
开始以每秒
�
2
v
2
单位的速度运动,并且可以随时改变运动方向。请计算出至少需要多少时间,才能使得线段上的每个位置都至少被一名旅行者经过。
Description
不难想到有三种情况(为了方便讨论,我们设
�
1
≤
�
2
p
1
≤p
2
):
�
(
�
)
A(B) 独自走完全程,原因是
�
(
�
)
B(A) 走得实在太慢。
�
A 向右走到底,
�
B 向左走到底,两人所花的时间取一个最大值。
�
A 走完左边的一段区间,
�
B 走完右边的一段区间(两段区间的连接点在
[
�
1
,
�
2
]
[p
1
,p
2
] 内),然后对两人所花的时间取一个最大值。我们钦定这个连接点为
�
P,显然他们各自有两种走法:
先走到
�
P,再走向边界。
先走到边界,在走向
�
P。
不难发现,
�
,
�
A,B 的走法互相之间没有影响,不一定全部都要采用同一种走法,只要各自走最优的就行。同时随着
�
P 位置的变化,不仅花费的时间会变,他们各自采用的走法也会有所变化,那就不可以简单地将总路程除以总时间再特判一下草草了事。我一开始还以为直接分类讨论一下就可以了。
不过可以肯定的是,随着
�
P 的不断右移,不管
�
A 采用什么走法,他所花的时间都是单调递增的(总不能我走的路程多了,花的时间反而少了吧);
�
B 也同理。
同时又想到花费时间可能偏多的本质是
�
,
�
A,B 其中一人在走完自己的区间后另一人还未走完,那么这个人明显可以再多走一点。因此我们考虑二分出一个
�
P,使得花费时间最少,即两人恰好走完自己的区间。
最后的答案就在这三种情况里取一个最小值即可。代码时间复杂度
�
(
�
log
�
)
(
�
=
1
0
11
)
O(TlogP) (P=10
11
)。
不得不说这是一道很巧妙的二分题。
Code
#include
using namespace std;
double n, p, v, pp, vv, ans, mid;
int read(){
int x = 0;
char a = getchar();
while(a < '0' || '9' < a) a = getchar();
while('0' <= a && a <= '9') x = (x << 1) + (x << 3) + (a ^ 48), a = getchar();
return x;
}
bool check(){
double t = (min(mid - p, p) + mid) / v, tt = (min(pp - mid, n - pp) + n - mid) / vv;
if(t < tt){
if(tt < ans) ans = tt;
return 1;
}
if(t < ans) ans = t;
return 0;
}
int main(){
for(int _ = read(); _ >= 1; _ --){
scanf("%lf%lf%lf%lf%lf", &n, &p, &v, &pp, &vv);
if(p > pp) swap(p, pp), swap(v, vv);
ans = min((min(p, n - p) + n) / v, (min(pp, n - pp) + n) / vv); //一人走完
ans = min(ans, max((n - p) / v, pp / vv)); //互相穿过
double lft = p, rt = pp;
while(rt - lft >= 0.0000001){
mid = (lft + rt) / 2;
if(check()) lft = mid + 0.0000001;
else rt = mid - 0.0000001;
}
printf("%.6f\n", ans);
}
return 0;
}