ACL Beginner Contest F - Heights and Pairs
解法
まずは入力を各身長ごとの人数として集計します。 を身長が である人の人数とします。 であり、 人であるような身長を無視すれば身長の種類数は 以下です。
条件を満たす組み方をDPなどで直接数えようとすると、 から落ちず厳しいです。
包除原理を適用します。同じ身長で組んでいるペアを「違反ペア」と呼ぶことにして、違反ペアを 個以上集めた集合を とします。「 に含まれるペアは必ず組んであって、その他のペアは自由に組まれているような組み方の数」を とすると、答えは包除原理から
と計算できます。集合 の取り方は膨大にあるので のサイズごとに集計することにします。
まずはそれぞれの身長ごとに独立に、違反ペアをいくつか組む組み方を数えます。以下の値を計算します。
身長が の人の中で、違反ペアをちょうど 組だけ組むような組み方。(それ以外の人の組み方はまだ決めない)
これはまず、違反ペアに参加する人を選ぶのが 通り。その中で違反ペアを組む組み方は、
- まず 人適当に誰かを選び、その人の相方を残りの 人の中から選ぶ。
- また 人適当に誰かを選び、その人の相方を残りの 人の中から選ぶ。
- …
と考えると、二重階乗を用いて 通りと計算できます。この は から まで取り得ます。
各身長ごとに求めた数列 を全てマージします。身長 の人の違反ペアが 組、身長 の人の違反ペアが 組…という組み方を合わせると、合計違反ペアが 組であるような組み方が 通り得られます。これを全ての について計算するので、これは数列をFFT(NTT)でマージすることに相当します。
これを最後までマージすると、以下の値を示す数列が得られます。
全ての人の中で、違反ペアをちょうど 組だけ組むような組み方。(それ以外の人の組み方はまだ決めない)
これに残りのペアの組み方 を掛けると、「 であるような についての の和」が得られます。これを包除原理の式に従い合計すると答えになります。
あとはNTTのマージにかかる計算量を落とす必要があります。各身長に対する の長さは であり、なので、全ての数列の長さの和は 以下です。
何も考えずにマージすると、長い数列に短い数列を何回もマージするような場合に最悪で の計算量が掛かってしまいます。
マージの順番を工夫して、以下の二分木のようにマージをするとどうでしょうか。
二分木の1段分のマージでは、長さの和が 以下である数列たちが1度ずつNTTでマージされるので、合計で計算量 になります。そして二分木のようにマージすると 段で全てマージし終わるので、全体計算量 でマージできます。
数列の本数が2冪でないときはトーナメントのシードのような扱いをします。また二分木でのマージとは挙動が少し異なりますが、数列たちをキューに入れて「先頭から 本取り出してマージした結果を末尾に入れる」という処理を 本になるまで繰り返しても同様の計算量が達成できます。