CS Academy #38 - Tree Antichain (Hard)
問題
問題概要
頂点数の木が与えられる。長さの順列を考え、それを頂点に対応させる時、との間にいずれも辺がないような順列の中で辞書順最小のものを求めよ。
- テストケース数 :
- テストケース全体でのの和はを超えない
アイデア
同じラウンドに(Easy)というのも存在していたが、そちらは辞書順最小という条件がはずれたもの。このときはグラフがスターでなければ構成可能。適当に根を決めて根からの距離の偶奇で二部グラフになるので、偶数側を並べて奇数側をならべればよい(変わり目だけは辺がないように気をつける)。
この中でも辞書順最小のものが知りたい、と言う話になってくる。グラフがスターだと構成できないという話があったが、これをもう少し広げると、ある順列という順列を考えたときに、前から順番にの頂点から順に削除をしていくという操作を考えると、この操作の過程で、一度もグラフがスターになることがなければこの順列はvalidなものであるということが言える。以下でこれを確認する:
まず、木を二部グラフと見なす(適当な根からの頂点の偶奇とかで簡単に色分けできる)。黒と白に色分けすることにしよう。
上で書いていたことを少し言い換えて、今、という順列があるような状況を考える。この個の頂点を取り除いたグラフがスターになっていなければその順列はそれ以降、番目まで妥当なものを構築できることを示す。
今、の頂点は白、つまりと接続している頂点は全部黒であるとする。残っているグラフが非連結ならば、以降は白い頂点を全部並べて、その後に黒い頂点を並べればそれは問題なく構築できる。もし、残っているグラフが連結ならば(スターではない)、白い頂点を全部並べて、その後に黒い頂点を並べるのだが、白から黒への切り替わるタイミングで辺があるような2頂点を選ばないように気をつければ良い(そしてスターでなければこのようなペアは必ずある)(証明終)。
この事実が正しいということがわかると、この個の順列は前から順番に決定していくことができるようになる。
番目の頂点をどれにするかを決める段階を考えると、まだ使われていない頂点をsetで管理しながら、頂点番号が小さい順に追加してよいかをチェックしていく。チェックすべきことは、と接続していないこと、もしその頂点を選んだ後に残るグラフがスターになっていないこと(これは、残っている頂点の次数をmultisetで管理することでチェックできる)の2つになる。
これをチェックしながら貪欲にやっていくことで構築できる。コードを書いてみるとになっているように見えるがそうではない。失敗するパターンとしては、隣接してるときとスターになってしまう時がありえるが、まず隣接していて失敗するというのは全部を構築する過程で回しか起こらない。またスターになって失敗する状況というのは、グラフから1点を取り除くとスターになるようなグラフについて、そのような取り除くと残るグラフがスターになるような頂点は番目のステップにおいてたかだか2点しかないということを考えると、setなどの操作を考慮して全体でで処理ができる。
実装(C++)
続きを読むCF 765 F - Souvenirs
問題
問題概要
長さの数列が与えられる。
とが与えられ、 の最小値 を答えよというクエリが個与えられるので、それらに答えよ。
アイデア
まず、絶対値を考えるのはややこしい。数列をreverseしてindexもreverseして同じクエリに対して答えれば題意の条件を満たすことが出来るので、という条件の部分についての最小値を考えることにする。
クエリの先読みを考えよう。の昇順でクエリを処理していくことにする。今、を固定した状況に対して、ans[i] = 左端がiの時のクエリの答え
としてを右に動かしながら配列を更新し、クエリに答えていく。
さて、今が右に動いてに注目しているという状況で、はどのように更新されるか考えてみる。いま、という条件の部分についてのみ考えるという条件があるので、からスタートしてを少しずつ減少させていき、初めてになるような位置をまずは見つける(とする)。
まず、このようなを見つける事ができれば、について、必ずとを含むことから、ans[k]は以下であることは分かるので、最小値を更新出来る。ここで、ansという配列を遅延segment tree上で管理すれば、区間のupdateなどはで処理出来る。今回は区間のupdateの左端が0で固定なので、普通のsegment treeで良さそう。
その次にこのupdate処理を入れるべき箇所としては、更にを左に動かしていき、となるような箇所である。の方がよりもに近い値を取ることから、について、ans[k]はで最小値を更新出来る。
よって、を右に動かしながら、そのたびにをからに動かしてsegtreeを更新していけばで出来るように見えるけど、これでは不十分なので、更に高速化することを考える。
さて、から次のを見つける部分について考えてみる。このとき、という値で最小値が更新される条件を考えてみると、とを含む区間というのは必ずも含んでいることは、先の操作を考えると明らかであるので、最小値が更新される条件として、となる必要がある。これを式変形するととなる。このの制約によって、1つの固定されたに対して先のようなupdateの処理は高々回となる。これは、十分に速く動作する。この問題を解くためにはこの考察がなければ解けない。言われてみると当たり前のことだが、このように条件を見て式変形することで実際に計算量を落とすことが出来るとても面白い問題だった。