パナソニックプログラミングコンテスト(AtCoder Beginner Contest 186)E - Throne
E - Throne
を満たす最小の整数を求めよ。
拡張ユークリッドの互除法による逆元計算
まず、与式をと変形することでが求まれば、問題を解くことができるとわかります。
ここで、におけるの逆元の存在条件は、とが互いに素であることです。
NとKが互いに素のとき
とが互いに素のとき、の逆元は、拡張ユークリッドの互除法(extGCD)によって求まります。
拡張ユークリッドの互除法による逆元の求め方
を満たす整数をextGCDによって求め、この式をの式に変形すると、となることから、つまり、aがの逆元になっています。
NとKが互いに素でないとき
とが互いに素ではない場合、の逆元は存在しません。では、今回の問題を解くことはできないのでしょうか。サンプルをみればわかる通り、そういうわけではありません。
とが互いに素でないなら、互いに素にしたくなります。とを、で割った結果をそれぞれとおくと、とは互いに素です。
これらとをextGCDにかけることで、を求めることができました。
与式に戻りをそれぞれに置き換え、
とすれば、が整数、すなわちがの倍数である場合に、先ほどextGCDによって求めたを用いてを求めることができます。
がの倍数ではない場合、解はありません。
実装
long long extGCD(long long a, long long b, long long &x, long long &y) { if (b == 0) { x = 1; y = 0; return a; } long long x2, y2; long long d = extGCD(b, a%b, x2, y2); x = y2; y = x2 - (a / b) * y2; return d; } long long mod_inv(long long a, long long m){ ll x, y; ll d = extGCD(a, m, x, y); if(d != 1)return -1; if(x < 0)x += m; return x; } long long safe_mod(long long a, long long m){ return (a%m+m)%m; } void solve(){ ll N, S, K; cin >> N >> S >> K; ll g = gcd(N, K); ll inv = mod_inv(K/g, N/g); if(S % g != 0){ cout << -1 << endl; } else{ ll ans = S/g * (-inv); ans = safe_mod(ans, N/g); cout << ans << endl; } }
中国剰余定理
という式から、以下のような連立合同式を考えます。
中国剰余定理によって、この連立方程式を解きます。(解がない場合もあります。その場合は、今回は-1を出力して終わりです)。
このとき、との最小公倍数をとすると、
の場合、
の場合、
となります。
中国剰余定理で求まるは、周期であり、その間に唯一のものであるから、当然今回の解に不適切なものも出てくる(未満など)ので、その場合は、一個先の解を使えばよい(を足しているのはそのためである)。