公式

N - 平面徒競走/Planar Footrace 解説 by yuto1115

解説

T 君のスタート地点を \(T\)、A 君のスタート地点を \(A\)、ゴール地点を \(G\) とおきます。また、\(2\) 地点 \(X,Y\) 間の距離を \(d(X,Y)\) と表記します。

T 君が徒競走に負けない条件は \(\frac{d(T,G)}{V_T} \leq \frac{d(A,G)}{V_A} \Leftrightarrow \frac{d(A,G)}{d(T,G)} \geq \frac{V_A}{V_T}\) です。

最初からこの条件を満たすゴール地点 \(G\) の集合を考えるのは難しいので、座標平面上で \(\frac{d(A,X)}{d(T,X)} \geq \frac{V_A}{V_T}\) を満たす点 \(X\) が作る領域を \(D\) とし(\(G\) と異なり \(X\) は平面上の任意の場所を動きます)、まず \(D\) の形状を考えた上で、次に \(D\)\(y\) 軸の共通部分を考える方針をとることにします。

\(D\) の形状は、\(V_T\)\(V_A\) の大小関係によって \(3\) 通りの場合に分かれます。

(i) \(V_T=V_A\) の場合

線分 \(TA\) の垂直二等分線を \(l\) とすると、平面を \(l\) で分割してできる \(2\) 領域のうち点 \(T\) を含む方が \(D\) です(境界線 \(l\) を含む)。よって、\(l\)\(y\) 軸と平行(\(\Leftrightarrow T_y=A_y\))かつ \(D\)\(y\) 軸を含まない場合に限り答えは \(0\) であり、そうでなければ答えは inf です。

(ii) \(V_T<V_A\) の場合

\(\frac{d(A,X)}{d(T,X)} = \frac{V_A}{V_T}\) を満たす点 \(X\) の集合は、点 \(T\) を内部に含むような円となることが知られています(アポロニウスの円)。具体的には、線分 \(TA\)\(V_T:V_A\) に内分する点を \(P\)\(V_T:V_A\) に外分する点を \(Q\) としたとき、線分 \(PQ\) を直径とする円になります。よって、この円を \(C\) とおくと、\(C\) の円周及び内部が \(D\) になります。

円と直線の共通点は高々 \(2\) つですから、\(C\)\(y\) 軸の共通点も高々 \(2\) つであり、答えは

  • \(C\)\(y\) 軸の共通点が \(1\) 個以下のとき、\(0\)
  • \(C\)\(y\) 軸の共通点が \(2\) 個のとき、その \(2\) つの共通点間の距離

です。よって、あとは \(C\)\(y\) 軸の共通点が求められればよいですが、これは \(C\) の中心の座標を \((C_X,C_Y)\)、半径を \(r\) とすると、

  • \(|C_X| > r\) のとき存在しない
  • \(|C_X| = r\) のとき \((0, C_Y)\)\(1\)
  • \(|C_X| < r\) のとき、\(d=\sqrt{r^2-{C_X}^2}\) として、\((0, C_Y\pm d)\)\(2\) 点(三平方の定理より)

となります。

(iii) \(V_T> V_A\) の場合

先述の (ii) の場合から T 君と A 君の立場が入れ替わっていると考えると、A 君が先に到達できるような地点の集合がある円の内部として表されることがわかります。この円の配置に関わらず、\(G\)\(y\) 座標が十分大きい/十分小さいときは T 君が先にゴールできるので、答えは inf です。

以上より、適切な場合分けと演算を行うことで本問題を \(O(1)\) で解くことができます。下記の実装例では誤差を抑えるためなるべく整数のまま計算を進めています。

実装例 (C++) :

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

int main() {
    ll tx, ty, vt, ax, ay, va;
    cin >> tx >> ty >> vt >> ax >> ay >> va;
    if (vt > va) {
        cout << "inf" << endl;
    } else if (vt == va) {
        if (ty != ay) {
            cout << "inf" << endl;
        } else {
            cout << (abs(tx) <= abs(ax) ? "inf" : "0") << endl;
        }
    } else {
        ll dx = tx - ax;
        ll dy = ty - ay;
        ll f = va * va - vt * vt;
        // _r = (r * f) ^ 2, _cx = (cx * f) ^ 2
        ll _r = va * va * vt * vt * (dx * dx + dy * dy);
        ll _cx = ax * f + dx * va * va;
        _cx *= _cx;
        if (_r < _cx) {
            cout << 0 << endl;
        } else {
            cout << fixed << setprecision(15) << sqrt((double) (_r - _cx) / (f * f)) * 2 << endl;
        }
    }
}

投稿日時:
最終更新: