ABC145 D - Knight
目次
# 問題
https://atcoder.jp/contests/abc145/tasks/abc145_d
整数, が与えられる.
から出発し、、またはの移動のみを繰り返しまで到着するのに何通りの道があるかを答える問題.
到着不可能の場合は0通り.
# 解説
, の最大値がなので、以下の計算量ではければ間に合いません.
移動の仕方が2通りしかないので、深さ優先探索でマス目を移動していき到達するケースを数えていくという解が思いつきますが、計算量はで間に合いません.
そこで移動の制約に着目し、、の移動をする回数をそれぞれ, とおくと、以下の連立方程式が得られます.
- (1) 移動するごとにxとy合わせて3ずつ増加するので、は3の倍数であるはずです.
- (2) 移動回数なので0以上です.また、必ず1は移動するのでXの最大値以下です。
- (3) (4) 移動量を回数分だけ繰り返すとに到着するという意味です.
(3) (4) を、について変形して、
ここまででの移動を回、を回するということが求まりました.
次にゴールまでの道順を求めますが、これは個のと個のの並べ方の数だけあることになります.
全移動は回で、その中で個の位置を決めていくのでの組み合わせです.
あとは、これを以下の計算量で求めるだけです.
は高校数学でも出てくるように以下の式で計算できます.
例えば、なら、
ここでは3つのファクトリアルから計算出来ることが分かります.
これらはすべてまとめてボトムアップ動的計画法でで計算出来ます.
で割った答えを求めるので、割り算のところだけそのままでは計算
できないので、割り算の代わりに逆元との積に置き換える必要があります.
逆元は拡張ユークリッドというアルゴリズムでで求める事ができます.
# 計算量
# 解答
Int X_, Y_;
Int fac[MAX_M];
void input() {
cin >> X_ >> Y_;
}
void buildFact() {
fac[0] = fac[1] = 1;
for (int i = 2; i < MAX_M; i++){
fac[i] = fac[i - 1] * i % MOD;
}
}
// Extended Euclid
Int modinv(Int a, Int p) {
Int b = p, u = 1, v = 0;
while (b) {
Int t = a / b;
a -= t * b; swap(a, b);
u -= t * v; swap(u, v);
}
u %= p;
if (u < 0) u += p;
return u;
}
Int nCk(int n, int k){
if (n < k) return 0;
if (n < 0 || k < 0) return 0;
return fac[n] * modinv((fac[k] * fac[n - k] % MOD), MOD) % MOD;
}
void solve() {
buildFact();
if ((X_ + Y_) % 3 != 0) {
cout << 0 << endl;
return;
}
Int m = (2 * X_ - Y_) / 3;
Int n = (Y_ - m)/2;
if (m < 0 || n < 0) {
cout << 0 << endl;
return;
}
cout << nCk(m + n, n) << endl;
}
int main(void) {
input();
solve();
return 0;
}