AtCoder Beginner Contest 145の解説[A to D]
A - Circle
入力:
出力:
制約:
半径の円の面積:
半径の円の面積:
ゆえに、求めるものは である。
writeln(r*r);
感想: タイピングゲーム FA速すぎ
B - Echo
入力:
出力: 条件を満たせば'Yes', そうでなければ'No'
制約:
- は英小文字から成る
問題文にある通り、「Sが、ある文字列を二度繰り返したものであるか否か」を判定します。Sの前半部分と後半部分が一致するか確かめればよいです。
if(S[0..N/2] == S[N/2..$]) writeln("Yes"); else writeln("No");
感想: やるだけ
C - Average Length
入力:
出力: 経路の長さの平均値
制約:
- ならば
たとえば3個の町がある場合(N = 3のとき)、考えうる訪れ方は
- 1 -> 2 -> 3
- 1 -> 3 -> 2
- 2 -> 1 -> 3
- 2 -> 3 -> 1
- 3 -> 1 -> 2
- 3 -> 2 -> 1
の 通りあります。問題文にある通り、このような訪れ方は通りあります。
上記のような順列を列挙したい…… そんなあなたには……ジャジャーン ☆nextPermutation☆ (C++ではstd::next_permutation)
$ rdmd --eval="auto a = [1,2,3]; do{writeln(a);}while(nextPermutation(a));" [1, 2, 3] [1, 3, 2] [2, 1, 3] [2, 3, 1] [3, 1, 2] [3, 2, 1]
こんな風にいい感じにやってくれます!これを利用しましょう。
void main() { auto N = readln.chomp.to!int; double res = 0; alias Pair = Tuple!(int, "ind", int, "x", int, "y"); Pair[] pairs; foreach(i; 0..N) { auto ip = readln.split.to!(int[]), x = ip[0], y = ip[1]; pairs ~= Pair(i, x, y); } do { double tmp; foreach(i; 0..N-1) { auto p1 = pairs[i], p2 = pairs[i+1]; tmp += dis(p1.x, p2.x, p1.y, p2.y); } res += tmp; } while(nextPermutation(pairs)); writefln("%.8f", res / fact(N)); } int fact(int n) { if(n == 0) return 1; else return fact(n-1) * n; } double dis(double x1, double x2, double y1, double y2) { return sqrt(pow(x1-x2, 2) + pow(y1-y2, 2)); }
の距離を求める関数disを用意しました。
これは を計算します。
こうやって全通りの順列を試して、最後に で割ればよいです。
という制約のため試すべき場合は高々 通りだけしかありません。したがって、この問題を の解法を用いて解くことができました。
感想: 基本に戻って全部試すことが大事な問題でした。私はデバッグ出力を消し忘れて1WAしました。かなしい。
D - Knight
入力:
出力: -> までの行き方の場合の数を10億7で割った余り
制約:
dp[x][y]: までの行き方の場合の数(mod 10億7)として、
dp[x][y] = dp[x-1][y-2] + dp[x-2][y-1]
dp[0][0] = 1
(添字が範囲外なら配列の値は0として考える)
とすればいい感じに解けそうですが、 は最悪の場合 になるので間に合いません。
しかし、この解法を組めば小さな の場合の問題を解くことができて、眺めるとパスカルの三角形が関係していることがわかります。
実際、 -> or という遷移だったらまさにその通りになります。
求めるものは、 が整数になるとき 、そうでないときは となります。
nCr mod p の高速な求め方は、下記ブログを参考にしてください(けんちょんさんのブログは参考になることばかりなので、読者になりましょう)。↓↓↓
void main() { auto ip = readln.split.to!(int[]), X = ip[0], Y = ip[1]; auto k = (X + Y) / 3; if((X + Y) % 3 != 0) { writeln(0); return; } new COM(max(X, Y) + 1, 1000000007).nCr(k, X-k).writeln; } class COM { long[] fact, finv, inv; private ulong MAX; private ulong MOD; this(ulong MAX, ulong MOD) { this.MAX = MAX; this.MOD = MOD; fact = new long[](MAX); finv = new long[](MAX); inv = new long[](MAX); // initialize the arrays fact[0] = fact[1] = 1; finv[0] = finv[1] = 1; inv[1] = 1; foreach(i; 2..MAX) { fact[i] = fact[i - 1] * i % MOD; inv[i] = MOD - inv[MOD%i] * (MOD / i) % MOD; finv[i] = finv[i - 1] * inv[i] % MOD; } } ulong nCr(long n, long k) { if(n < k) return 0; if(n < 0 || k < 0) return 0; assert(n < MAX && k < MAX); return fact[n] * (finv[k] * finv[n - k] % MOD) % MOD; } ulong factorial(long n) { return fact[n]; } }
感想: ABCでも逆元を用いた高速なnCrを求めるアルゴリズムが要求されるようになったんですね。私はコンテスト終了20分前にパスカルの三角形に気づき、そこからnCrの高速な求め方を調べて実装しました。レートは落ちました。──完──
A: Submission #8461953 - AtCoder Beginner Contest 145
B: Submission #8464057 - AtCoder Beginner Contest 145
C: Submission #8470993 - AtCoder Beginner Contest 145
D: Submission #8486307 - AtCoder Beginner Contest 145