結果

問題 No.62 リベリオン(Extra)
ユーザー codershifthcodershifth
提出日時 2015-08-22 10:01:20
言語 D
(dmd 2.107.1)
結果
WA  
実行時間 -
コード長 4,300 bytes
コンパイル時間 965 ms
コンパイル使用メモリ 104,152 KB
実行使用メモリ 4,372 KB
最終ジャッジ日時 2023-09-02 20:20:47
合計ジャッジ時間 2,323 ms
ジャッジサーバーID
(参考情報)
judge14 / judge11
このコードへのチャレンジ(β)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 1 ms
4,368 KB
testcase_01 AC 1 ms
4,372 KB
testcase_02 WA -
testcase_03 WA -
testcase_04 WA -
権限があれば一括ダウンロードができます

ソースコード

diff #

import std.stdio;
import std.string;
import std.array;
import std.typecons;
import std.typetuple;
import std.container;
import std.algorithm;
import std.conv;
import std.math;
import std.format;

alias TypeTuple tie;

void readlnToken(T...)(auto ref T args)
{
    import std.stdio;
    import std.conv;
    import std.string;
    import std.array;

    auto line = split(readln().strip);
    foreach(ref arg; args)
    {
        arg = to!(typeof(arg))(line[0]);
        line = line[1..$];
    }
    assert(line.empty()); // got all token??
}

T extgcd(T)(T a, T b, ref T x, ref T y)
{
    T d = a;
    if (b != 0)
    {
        d = extgcd(b, a%b, y, x);
        y -= (a/b)*x;
    }
    else
    {
        x = 1; y = 0;
    }
    return d;
}

T gcd(T)(T a, T b)
{
    if ( abs(a) < abs(b) )
        swap(a,b);
    if ( b == 0 )
        return a;
    return gcd(b, a%b);
}

void solve()
{
    int Q;
    readlnToken(Q);

    foreach(i; 0..Q)
    {
        // 弾丸が反射するたびに Mami の位置が壁の向こう側に反転すると考えると
        // 弾丸はまっすぐ飛ぶと考えられる
        //             M              |  M
        //              \             | /
        // -----+      --\-+       ---+----
        //    /\|         \|          /
        //   M /|  =>     /|  ->     /|
        //    / |        / |        / |
        //
        int W,H,D,Mx,My,Hx,Hy,Vx,Vy;
        readlnToken(W,H,D,Mx,My,Hx,Hy,Vx,Vy);

        // 弾丸の発射方向を正方向に変換
        if (Vy < 0)
        {
            My = H - My;
            Hy = H - Hy;
            Vy = -Vy;
        }
        if (Vx < 0)
        {
            Mx = W - Mx;
            Hx = W - Hx;
            Vx = -Vx;
        }

        // 弾丸が水平・垂直に打たれるときは簡単に計算できる
        if (Vx == 0)
        {
            if ( Hx == Mx && ( (My > Hy && My-Hy <= D*Vy) || (My < Hy && 2*(H-Hy)+Hy-My <= D*Vy) ) )
                writeln("Hit");
            else
                writeln("Miss");
            continue;
        }
        if (Vy == 0)
        {
            if ( Hy == My && ( (Mx > Hx && Mx-Hx <= D*Vx) || (Mx < Hx && 2*(W-Hx)+Hx-Mx <= D*Vx) ) )
                writeln("Hit");
            else
                writeln("Miss");
            continue;
        }

        auto g = gcd(Vx,Vy);
        // Vx,Vy を互いに素にして D を最小公倍数倍しておく
        D *= g;
        Vx /= g;
        Vy /= g;

        // あたり判定関数
        bool hit(int tx, int ty)
        {
            import std.bigint;
            //
            // tx = T*Vx + Hx (mod 2*W)
            // ty = T*Vy + Hy (mod 2*H)
            //
            // なるような T <= D が見つかればよい
            //
            // tx-Hx-T*Vx=2*W*n
            // ty-Hy-T*Vy=2*H*m
            //
            // (2*W*Vy)*n-(2*H*Vx)*m = Vy*(tx-Hx)-Vx*(ty-Hy)
            //   a            b                 c
            // この不定方程式の解 (n,m) をもとめて T を計算する。
            //
            //
            BigInt a = 2*W*Vy;
            BigInt b = -2*H*Vx;
            BigInt c = Vy*(tx-Hx)-Vx*(ty-Hy);

            // a*n+b*m = c
            auto g = gcd(a,-b);
            // 不定方程式が解をもたないケース
            if (c % g != 0)
                return false;
            a /= g;
            b /= g;
            c /= g;

            BigInt x,y;
            // a*n+b*m = c をとく
            // まず a*x+b*y = 1 なる (x,y) を求めて c 倍すればよい
            if (extgcd(a,b,x,y) != 1)
                return false;

            auto m = y*c%a; // a の倍数分は n にゆずる
            auto T = (ty-Hy-2*H*m)/Vy;

            // a*(n+b)+b*(m-a) = c で gcd(a,b)=1 より
            // 2*H*a/Vy が周期になる
            auto period = 2*H*a/Vy;
            T %= period;
            // 必要なら period を加えれば正になる
            if (T < 0)
                T += period;

            return T <= D;
        }
        // 各反転座標で当たるかチェックする
        if ( hit(Mx,My) || hit(2*W-Mx,My) || hit(Mx,2*H-My) || hit(2*W-Mx,2*H-My) )
            writeln("Hit");
        else
            writeln("Miss");
    }
}

void main()
{
    solve();
}
0