Um_nik回のCが面白かったので。
あと、自分の解法が想定解と違ったっぽいので。
問題概要
ビットの二進数 が 個与えられる。
のとき頂点 に辺を張ったグラフの連結成分数を求めよ。
制約
- はdistinct
想定解法
writerのeditorial
まず、 は が に包含されてる()というのと同値です。
個の頂点 と、 個の頂点 を用意する。
はそのまま入力の数に対応する頂点で、 は高速ゼータ変換的な遷移用の頂点。
ここに以下のように有向辺を張っていく。
- →
- が のビットを1つ から に置き換えた数であるとき、 →
- →
こうすると、 ⇒ のパスがあることが と同値になる。
そんで、「まだ訪れてない頂点からdfsをして到達可能な頂点をvisitedにする」という操作を行えた回数が連結成分数になる。
この解法のミソは、 ってのが に対して対称なので、 ⇒ のパスがあるなら必ず ⇒ のパスもあるってところ。
普通、有向グラフでDFSしても連結成分数なんて求まらないし、そもそも"連結"の定義がよく分からないんだけど、この性質があることによって の中での連結成分が無向グラフと同じように定義できてる。
なんていうか、 が無向辺的な役割をしてると考えるといいのかな。
僕はこのへんに気づかなくて有向グラフにしてもしょうがないしな・・・って思ってこの方針を捨ててしまった。
別解 by snuke
まず計算量の悪い解法として、以下のものは思いつきます。
- 高速ゼータ変換で = 「 が に包含されるような のリスト」を求める。
- の各要素と をUnionFindでくっつけて、連結成分数を求める。
ここで、なんとかして に持たせる値をリストじゃなくて1つの数のみにできないかというモチベーションを得ます。
「 の各要素と 」をくっつけるかわりに、「 の代表1つと 」と「 の全要素を互いに」くっつけることにすれば、 には代表1つを記録しておくだけでよくなりそうです。
整理していくと以下のような解法になります。
- 前処理として、高速ゼータ変換で = 「 を包含するような が存在するかどうか」を求める。
- 高速ゼータ変換で = 「 が に包含されるような の代表(例えばmaxなど)」を求める。
- ただし、 の場所は
- 高速ゼータ変換中に2つの頂点 をマージしたくなった場合は、 をUnionFindでくっつけたのち代表のみを残す
- ( ということは、なんらかの頂点 が存在して、 と の2つの辺が張られるはずなので は同じ連結成分になる)
- と をUnionFindでくっつけて、連結成分数を求める。
まとめ
どちらの方針の方が思いつきやすいのかはよくわかりませんが、どちらにせよ面白い発想が必要とされる良問だと思いました。
ちなみにセットとしては、LGM writerにしてはEFが簡単、というかC~Fに難易度差があまりないように感じたが、終盤ジャッジがつまったりDで苦しむ人が多かったりで4完すらそれほど多くなかったらしい。
他の問題もD以外は普通に面白い回だったと思う。Dもこういうのはサラッと通せるようにしとかないとね。