/ 最近 .rdf 追記 編集 設定 本棚

脳log[20221217]



2022年12月17日 (土) [AtCoder] 今日は HHKBプログラミングコンテスト2022 Winter(AtCoder Beginner Contest 282)があった。嫌なことからは目をそらして F 問題「Union of Two Sets」の精進について書くよ。最大ケース(N=4000)について考えると、ジャッジから与えられる任意の区間 4000×4001÷2 通りに対してこちらは事前に提示した 50000 個以下の区間を2つまで選んでその和集合をクエリと一致させなければいけない。およそ 800 万個の区間を予め提示しておけるなら考えることは何もないけど、上限は5万個である。区間をどう分けてどう組み合わせるか。まず最初に、幅1の区間は必ず 4000 個用意しておかなければいけない。そうすると幅2の区間は幅1の区間の組み合わせですべて作れる。じゃあ次は幅3の区間を用意してみようか(3998個)。幅3の区間が揃っていれば2つ組み合わせて幅3から幅6までの区間がすべて作れる。じゃあ次は幅7の区間を用意しよう(3994個)。次は幅15(3986個)、幅31(3970個)、幅63(3938個)、幅127(3874個)、幅255(3746個)、幅511(3490個)、幅1023(2978個)、幅2047(1954個)。だいたい4万個くらいの区間を用意すれば 1 から 4094 までの区間をすべてカバーできることがわかった。■提出 #37361344 (AC / 352 Byte / 1060 ms)。キモは WM。W 関数は表現したい区間の幅に対して、組み合わせるべき区間の幅を返す。連想配列 M は W 関数によって正規化された区間幅 w に対して、区間 [1,w] が何番目に提示した区間であるかが記録してある。■D 問題「Make Bipartite 2」と E 問題「Choose Two and Eat One」の精進はまだだよ。解けてないんだからしかたないね。残念なことがあったんだよ。■C 問題「String Delimiter」はいろんな解法が考えられて他の人の提出を見るのが楽しいかもね。でも現実問題として CSV を処理しようとするとどんな実装をした場合でも完璧には処理できない狂った仕様の CSV の実在が確認されそうで怖い。提出 #37330344 (AC / 83 Byte / 155 ms)。引用符で分割して奇数番目と偶数番目で処理を分ける、たぶんオーソドックスな方法。入力と出力で文字列長が変わらないことを考えると富豪的ではあるかも。スクリプトだから気軽に split とかやっちゃったけど。■D 問題。こちらのツイート「D dfsで各連結部分の色を塗りわける。色が塗り分けられないときは0。そうでない場合、ある連結成分については「異なる色の個数の積 - 辺の個数」、異なる連結成分通しは無条件で塗ってOK。辺を重複して数えないように注意。」を読む限り考察は完了していたと思う。提出 #37345235 (WA×20)。デバッグするだけだと思ったから E 問題へ行って、帰って来られなかったんだよね。それはそれでしかたがない。E 問題の、ああいう形の繋がり方から全域木が見えなかったことこそ問題。全域木が見えたら持つべきデータの形に頭を悩ませることもなかった。■D 問題。お風呂デバッグ完了しました! U(nite)関数において引数の2頂点のあいだに辺を張るべきところ、その根と根のあいだを辺で結んでしまっていた(と思う)。提出 #37379282 (AC / 604 Byte / 438 ms)。前々回の ABC280-F が重み付き UnionFind だったんだけど(20221203)、付随データが数値(距離)から二値(偶奇)になったから扱いが簡単になるわけではなく、むしろ距離の場合はベクトルの取り扱い方が使えるところ、0/1 の XOR では取り扱いが手探りになって頭がバグりがち。だから最初の提出ではいったん根からの距離を求めた上でその偶奇を利用するように途中から方針転換したんだけど、すでにバグっていた頭は根からの距離の計算もバグらせた。どっちでやってもバグらせたので AC 提出では当初の方針通り 0/1 の XOR を UnionFind に載せた。連結成分内の辺の数をわざわざ記録したけど、まとめて辺の総数(M)を引くのでいいらしい。■E 問題はもう種明かしを見てしまったので D 問題の UF 実装から難しい部分を取り除くだけでおしまい。提出 #37379631 (AC / 341 Byte / 296 ms)。■D 問題。頭がバグるなら BFS/DFS でやるなり頂点を倍加して普通の UnionFind をするなり(「偶数世界と奇数世界?」)、やりようがあったらしい。しかしまあ、バグる頭は足りない頭でもあるわけで。■F 問題。「FはSparse Tableを作れと書いてある」らしい。たしか蟻本でセグメント木、BIT に続けて紹介されていたのが Sparse Table だった。よくわからなかったので見なかったことにしたが。■蟻本を読み直した@風呂。セグメント木と BIT のあいだのコラムで Sparse Table が紹介されていた。こういうときに使うんだ、という強みが見えない紹介のされ方だったのであまり理解する努力をしなかったのだ。F 問題の解答を踏まえると Sparse Table のココロがわかった。BIT と比較するとメモリ消費が大きくて、ということは初期化と更新に時間がかかるということ。更新は区間幅(2べき)の和でフルビット(N)だろうか。取得に関して、BIT が対数個の要素を参照しなければいけないところ、Sparse Table は2個の参照で済む。しかしどの要素を参照するかを知るために前後の2べきを求めるのにビット長の線形時間(競プロ的には対数時間だったり「log は定数」だったりする時間)をかけると有利が消える。ビルトインの clz (count leading zeroes) が使えるとビット長の対数時間で2べきが求まったりするの? なんか上手い配置があったりするの? そんな感じで、参照に有利な条件が整っているときに強みがあるのが Sparse Table だと思いました。優先度は低いよなあ。■『ハッカーのたのしみ』を開いた。CLZ 相当の命令がある場合があるらしい。ということは __builtin_clz は「悪くて」ビット長の対数時間ということなん? 1命令のコストとかそのレベルからは「すんごく速そう」以外の感想はない。