あるエンジニアのAtCoder奮闘記

東京都港区にあるアミフィアブル株式会社のエンジニアが、AtCoderで解いた問題について振り返ったりしていく会社公認のブログです。

AtCoder ABC 132 D - Blue and Red Balls (Kotlin)

irisruneです。残念ながら今回のコンテストは不参加でした、問題を見るとD問題以降がかなり難しくなっているようですね。

問題

atcoder.jp

考察(+剰余周りの実装)面の問題ですが、ABCが新形式になって以降のD問題としてはかなり難しいと思います。ただしPythonなどを使う場合は実行時間に余裕があることが救いかもしれません(パスカルの三角形を使う場合は余裕がないようです)。

const val MOD = 1000000007

fun COMinit(m: Int): Pair<List<Long>, List<Long>> {
    val fac = mutableListOf(1L, 1L)
    val finv = mutableListOf(1L, 1L)
    val inv = mutableListOf(1L, 1L)
    for (i in 2 until m + 1) {
        fac.add((fac[i - 1] * i) % MOD)
        inv.add(MOD - ((inv[MOD % i] * (MOD / i)) % MOD))
        finv.add((finv[i - 1] * inv[i]) % MOD)
    }
    return Pair(fac, finv)
}

fun COM(m: Int, r: Int, fac: List<Long>, finv: List<Long>): Long {
    if (m < r) return 0L
    if (m < 0 || r < 0) return 0L
    return (fac[m] * ((finv[r] * finv[m - r]) % MOD)) % MOD
}

fun main(args: Array<String>){
    val (n, k) = readLine()!!.split(" ").map(String::toInt)
    val (fac, finv) = COMinit(Math.max(n - k + 1, k - 1))
    for (i in 1 until k + 1) {
        println((COM(n - k + 1, i, fac, finv) * COM(k - 1, i - 1, fac, finv)) % MOD)
    }
}

まず、剰余を考慮した二項係数の計算についてこちらのブログから拝借して実装したため紹介いたします。

drken1215.hatenablog.com

こちらのアルゴリズムを見た時に何かの機会に使うかもしれないと思っていましたがそれが今回でした。初めパスカルの三角形を用いて求めることを考えていたわけですが、差の剰余の求め方をど忘れしていたため断念しました(今思えば差がマイナスになる場合を考慮してアルゴリズムを組めばいいだけでした)。よく考えたらパスカルの三角形で差を求める機会はないですね…何を見ていたんでしょうか。

実装面は剰余を考慮した二項係数の計算ができるかが全てのため、残りは考察面の問題となります。

ここで白状しますと、自分がこの問題を解いた流れとしては、

  • まず問題文と入出力例1から考察したところ二項係数を使うと推測し、二項係数を計算する関数を実装しました。
  • 計算結果と入出力例2とを見比べた結果、二項係数同士の乗算を行うことで解が導き出せることを突き止めました。

となっており、最終的に問題文ではなく入出力例から解いてしまいました。_{N-K+1}C_iを用いるところまでは考察できたのですが、_{K-1}C_{i-1}を用いることに気付いたのは入出力例との比較を行ったときだったわけです。

そんなわけでちゃんとしたアルゴリズムについては公式解説以上のことは説明できそうにないです。場合の数は苦手ではない(むしろ得意な方)と自覚していたのですが、ノートも取らずに考察できるほど甘い問題ではなかったということですね。

なお、_{K-1}C_{i-1}を用いることに後で気付いたために前計算の配列長が足りなかったり、二項係数同士の乗算の後に剰余を取るのを忘れていたりでWAとREを出しています。ペナルティ込みでも(4完で)水パフォ中位くらいの時間だと思います。

雑記

  • このD問題であれば3完で水パフォ出てもおかしくなさそうですが最高でも緑パフォ上位のようです、参加者のレベルも全体的に上がっていますね。
  • 今週はC問題がかなり簡単でE問題がかなり難しいため、久々に過去問を扱うことになりそうです。
  • GoやPythonを触った後だと、Kotlinは想像以上に入出力が面倒という課題がありました。言語に理想を求めるとなかなかキリがないですね…