Search on the blog

2011年9月14日水曜日

蛙逢引 ~一次不定式で愛を探す~

蛙が逢引するロマンティックな問題。この問題は、問題文的にも、内容的にも好きだ。


ここに訳してくれている人がいる。http://d.hatena.ne.jp/kabus/20060728

以下問題の解き方。

x + mt = y + nt (mod L)より、
x + mt = y + nt + kL
(n-m) t + Lk = x - y となるような整数(t, k)の組を見つければいい。(問題では、最小の正の整数t*を求めることになるが、これは求まったtに対する代表元を考えればいい。)

このような解が存在する必要十分条件は、(x-y)がgcd(n-m, L)で割り切れることである。

まず、必要条件の証明。
もし、(x-y)がgcd(n-m, L)で割り切れない場合に、(t, k)が存在したとする。
このとき、
左辺 ≡ 0 (mod gcd(n-m, L))に対して、右辺 > 0 (mod gcd(n-m, L))となり矛盾。よって(x-y)がgcd(n-m, L)で割り切れない場合は、(t, k)の組は存在しない。

次に、十分条件の証明。
両辺をgcd(n-m, L)で割る。
(n-m)/gcd(n-m, L) をA、L/gcd(n-m, L)をB、(x-y)/gcd(n-m, L)をCとおくと、
At + Bk = Cと書ける。このとき、AとBは互いに素なので、Aを法にしたときのBの逆元が存在する。
つまり、y ≡ 1/B (mod A)となるようなyが存在する。
よって、1 - By ≡ 0 (mod A)。これは、(1 - By)がAで割り切れることを意味しているので、
x = (1 - By) / Aとなる整数xが存在する。これを変形すると、Ax + By = 1となり、この式を満たす整数(x, y)が存在する。これを両辺C倍してやると、A(Cx) + B(Cy) = Cとなり、(Cx, Cy)が(t, k)に他ならない。

ということで、解が存在するか否かは、gcdを出すだけで良いです。加えて、(n-m) t + Lk = x - y を満たす(t, k)の組は、(n-m)t' + Lk' = gcd(n-m, L)を満たす(t', k')を拡張ユークリッド互除法で出し、gcd(n-m, L)と(x - y)の比に合わせてスケーリングしなおせばOK。

最後に、問題となるのは、求まったtに対して、それと合同な最小の正の整数t*をどう出すか?そもそも何を法にして考えるか?これは、目盛りがL個付いている時計を(n-m)目盛りずつジャンプしていくと、何回ジャンプすると、同じ場所に戻ってくるかを考えればいいです。L/gcd(L, n-m)が答えです。ということで、tの法(L/gcd(L, n-m))における代表元を出せばOK。

以下回答ソース。

LL extgcd(LL a, LL b, LL &x, LL &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }

    int d = extgcd(b, a%b, y, x);
    y -= (a/b) * x;
    return d;
}

LL solve(LL x, LL y, LL m, LL n, LL L) {
    if (m == n)
        return -1;

    LL k, t;
    LL g = extgcd(n-m, L, k, t);
    if ((x-y) % g)
        return -1;

    LL ans = (x - y) / g * k;
    L /= g;

    return (ans % L + L) % L;
}

int main() {
    LL x, y, m, n, L;

    cin >> x >> y >> m >> n >> L;

    LL ret = solve(x, y, m, n, L);
    if (ret < 0)
        puts("Impossible");
    else
        printf("%I64d\n", ret);

    return 0;
}

0 件のコメント:

コメントを投稿