高速ゼータ・メビウス変換の実装例まとめ最後にまとめとして、各種変換の実装例を載せておく。コード外で宣言済みとする。verifyはしていないので、コピペは自己責任で。実装ミスがあれば報告していただけると助かります。大きい方から小さい方に加えられるので、更新順序的に問題ない。}}先程長々と解説した通り。}}非自明ではあるが、実はゼータ変換の更新の符号を反転させるだけでいい。1減るので符号が反転する、みたいなお気持ち。}}先と同じく符号を反転させるだけでいい。可換半群であるべきだと思われる。代数学にはあまり自信がないので注釈に留めておく。メビウス変換と呼ぶこともできる。分かりにくくなりそうなので割愛。↩この辺は記事によってはメビウス変換になっていたりして曖昧な部分だが、ここでは高速ゼータ変換/高速メビウス変換に従うことにする。強い性質をもつアーベル群である必要があると思われる。指数時間アルゴリズム入門の53ページに式変形が載っている。逆になっているので注意。展開すると成り立つことが分かる。(0-indexed)が与えられる。and)という記法を用いる。これは整数を「立っているbitからなる集合」と見たときに、集合における意味と一致する。立っているbitの数とする(絶対値ではない)。全列挙して上から2つを取る」という解法が考えられる。そこでこれを以下のような手法で高速化する1。以下の式で効率的に求められる。bitを1つ下ろしたものだけを列挙すれば十分となる。この手法を今回の問題に適用する。最大値を取るindexを持っていることに注意。なるような場合があるためである。集合から2つを取ることで求められる。実装時は要素の重複を消し忘れないように注意。0;}補足misteer.hatenablog.comこの手法は高速ゼータ変換と似て非なるものである。高速ゼータ変換を使った解法は本記事末尾のリンクを参照。AtCoderatcoder.jp問題のアルファベットからなるグリッドが与えられる。この行と列を自由に入れ替えることで、点対称にできるか判定せよ。やりたくなるが、当然それは許されない。そこで行の並び方だけ全列挙することにする。すると、「互いに逆の並びになっている列のペアで両端から埋めていく」という貪欲によって、列の並び方を全列挙することなく可能性判定ができる。奇数の場合は注意が必要。このとき真ん中の列に来るものは他とペアになっている必要はないが、代わりに自分自身とペアになる、すなわち回文になっている必要がある。落ちるが、まだ際どい。そこで行の並べ方を枝刈りする。点対称なグリッドにおいて、その対称な位置にある行を交換してもこれは点対称である。よって、両端から「上の行の方が小さくなるように」行番号を2つずつ決めていけば、それで可能性を全列挙できたことになる。以上の解法はEditorialそのままだが、難しいのは実装の方である。「両端から行番号を決めていく」というのが、いざ実装しようとすると結構面倒なことが分かる。奇数」のケースが本当に面倒くさい。通すまでに相当な数のバグを踏んだので、以下の実装例に自分が踏んだバグを一通り書いておく。実装例atcoder.jp以下では無名構造体内部で探索するような実装をしているが、ただ「グローバル変数を増やしたくない」というだけの理由なので、気にならない人は普通にいくつか関数を宣言すればいいと思われる。0;}ちなみに、行列挙を「next_permutation()で枝刈りする」として手を抜くとTLEになる。頂点完全グラフの各辺に対して、以下を満たすように正整数を割り当てる。戻るとき、そのパスの長さは常に偶数。このような割り当てで、割り当てられる正整数の最大値が最小のものを出力せよ。割り当てられた辺による誘導部分グラフが二部グラフをなす。「奇数長の閉路を持たない」と「二部グラフである」が同値であることが肝。1を割り当てる辺を決める。辺を使った方が後々楽なので、辺数が最多となる割り当てがしたい。頂点を半々に分割した完全二部グラフとなる。次に2を割り当てるが、残った辺に注目するとこれは2つのクリークをなしている。よってこれらに再帰的に上と同じ割り当てをしてやればいい。最小性の証明は思いつかなかったが、まぁ最適だろうと直感を信じて投げたら通った。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。