ABC334

5完。イライラを何かにぶつけたい。すぐにratedコンテストをやってくれ。(誰かが悪いわけではなく)不快すぎる。

A - Christmas Present

YesNoの出力をするやつをいじって作った。簡単な割に時間かかってるけど、やっぱ単語を2つも入力させるのは重いよな。Batだけ手で入れてGloveはコピペした(サンプルに両方あると信じていたので手で入れてもよい)。Batの綴りこれで合ってんの?と思って一瞬止まったけど、英語力も影響するんだよな。

B - Christmas Trees

すぬけ君は無限にクリスマスツリーを立てるのか?L,RからAを引いておく。すると、L以上R以下のMの倍数の個数を求める問題になる。個数を求める関数を1個書いて、f(R)-f(L-1)とかするのを思ったが、負の数がありえることとAを引いたことでllで扱える範囲をしっかり考える必要が出てくることから、やめた。整数に対し指定方向で最も近いMの倍数を求めるライブラリを持っているので、答えを変えないようにL,RをMの倍数に丸めて、そしたら引き算で簡単に答えが求まる。

0 10 4 6 みたいなケースで、Mの倍数に丸めると 0 10 10 0 になってLRが逆転することに気づいてなかった。これでも答えは正しく求まる。閉区間だからか。やっぱ半開区間しか勝たん。Rに1を足して半開区間とすれば、LもRも同じ方向のMの倍数にすればよくてコードがめちゃくちゃきれいになった。

C - Socks 2

奇数のケースがあるのヤバすぎる。手で試すと、使う靴下が決まったら近いの同士左端から貪欲でよさそう。で、それと同じコストで、揃ってる靴下をペアにすることもできそう。つまり、片方しかないやつだけ見ればいい。奇数のときどれを外すかは、いい性質なさそうなので全探索。両側から累積和すればいけるが、2個ずつなので考えるのがかなり大変。Kが偶数のケースは場合分けしておく。奇数なら、コストを後ろから2個ずつ見て差をvectorにつっこんでいき、前から見てそれをスタックみたいに使っていく。C問題でこれだけ技術を注ぎ込んだ実装になるのおかしい。まあこのくらいの実装はABCなら(嫌だけど)ありそうだけど、考察難度がやばいよな。難しさはARC並みだと思った。証明もできてないし。

D - Reindeer and Sleigh

なんか簡単すぎて見落としがないか心配になる。どのソリを選ぶかは、小さい順でいい。大きいの選んでたら小さいのに替えてそれでもいけるから。これがわかればもう難所は残ってなくて、累積和作って二分探索するだけ。upper_boundを使い、初めて引けなくなる台数を求める(1を引けば答え)。

E - Christmas Color Grid 1

UnionFindだけど、まあ実装は時間かかる。一応Gも読む。Gが上位互換というわけではなかった。隣とつなげるとき、自分も隣も'#'であることのチェックをいつも忘れる(今回もそれでサンプル通らなかった)。

まず、UnionFindをやりながら連結成分の個数を求めておき('#'の個数からmergeされた回数を引く)、次に各'#'について周囲にある連結成分の個数をleaderの種類数で求める。種類数を求めるのは、配列にleaderを入れてsortしてuniqueする(uniqueの戻り値で種類数がわかる)。あとは合計を求めて計算する。

F - Christmas Present 2

DPを考えたが、Kが大きいと間に合わない。ということで平方分割ができないか考えたが、家に戻る回数が少ないからといって解法は思いつかなかった(意外だった)。

区間minでDPできるやん。SWAGが使える。SWAGの(セグ木に対する計算量以外の)メリットとして、区間を明示的に書かなくていいというのがある(今回ならpopの条件だけ考えて書く)。「i個配り終えて家に戻ってきたときの最小値」みたいなものでDPする。このままだと最小値とる比較ができないけど、そこまでの距離(家に戻らず順番に家を回ったときの)を引くことで比較できるようになりそう。あとは、(サンタの)家に戻ったり家から次の場所へ行ったりする差分を調整してやればいい。合わない。添え字ガチャをしても合わないし、真面目に考えてみても全く不慣れな場所で全くわからない。そんなに難しくないことはわかっているのに、自分にはわからない。何度も声を上げた。

無限に混乱して死亡。サンプルが通ったのはコンテスト終了から30分後だった。大きい図を描くべきだった(N=3だと1と2に分かれて1になった側がわかりにくいのでN=4にするべきだった)。それをコンテストが終わるまでできなかった。

落ち着いて考えたらできた。ただ、これは気持ちの問題ではなく、最初から落ち着いて考える土地勘が自分にはなかった。この問題は簡単だという(正しい)判断からなめてかかったのが結果的には間違いで、相当地道にこの問題について(もしコンテスト中に通せる可能性がわずかでもあったとしたらそれをつかむために)学ぶ必要があった。

サンタの家から次の子供の家に行く距離を使うとき、思ってたより1個あとの家を見る必要があったというのが最も大きな間違いだった。それ以外にも、SWAGのノードを一部llからdoubleに直し忘れてたとか酷いミスがコンテスト終了時点で残ってたけど。*を+と書いてたりミスの個数多かったな。流れとして、ループの前に0をpushしておき、ループ内では必要ならpopして、距離の合計を更新し、そこで家に戻ったときの最小値を求め、それを使ってpushする、というのを書いていた。pushを先頭に持ってくるのは、ちょっとは考えていたけど、そのままでやっていた。入力を受け取りながらというのもだいぶ傲慢な(?)実装方針だった。求めた最小値を次のループで使うのが面倒だと思ってしまったが、まあそうするべきだった。そもそもきれいな方針引けない時点で自分にはそれを実装する能力がないんだよな。きれいなコードしか書いたことがない。それに頼った書き方しかできない。

G - Christmas Color Grid 2

undoできるUnionFindか?と思ったけどできない(計算量悪いけどできるらしい?)。もう少し考えて、関節点や橋か?と思い、その求め方を理解してないので解説を見た。

(追記)undo付きUnionFindでできた。経路圧縮すると、直接leaderにつないだ子のケアもしないといけないので、それはなし。経路圧縮しなければ変更がmerge内の2箇所だけになるので簡単に戻せる(過去のコードを持ってきてここまで復習)。H*Wマスを1列に並べ、[L, R)の区間の'#'だけ追加してない状態で関数を呼ぶようにして区間を半分にしながら再帰すると、各末端で答えが求まり、UnionFindの操作回数も各階層でO(H*W)となり、全体でlogが2つ付く(再帰の深さとUnionFindのmerge操作)。

(追記2)この機会にlowlinkを履修しようと思って3週間近くが経った。難しすぎて考えることができず、やろうと思いながら何もせず時間を無駄にしてきた。解説放送を見るといういつもやってる当然の手段を忘れていた。それを思い出して解説放送見たら理解が進む!それが昨日の深夜のことで、今日もだらだらしながら結局深夜になった。

そもそもDFS木の辺を使うとき根に向かって進んではいけないとかもわからないので、どっちが正しいのか考えながら解説を読まないといけなくて負荷がとんでもなく高かった。また、高々1つの後退辺でというのが、後退辺を使ったあともDFS木の辺を使っていいのかわからず、何も考えられなくなった。で、解説放送を見ると、ordは常に子のが大きいので、後退辺を使ったあとに移動する意味はなかった。横断辺がないというのも認識はしていたが、そのことを使うとここまで単純な構造になるのかと思った。

あとは自分でできる。もっとも、慣れない場所なので時間は相当かかる。各頂点のordは簡単にわかって、lowは自分から出てる後退辺の先のordと子孫のlowの最小値でわかる。あとは、lowが大きい子の数だけ、その頂点を除いたとき連結成分が増える。根を削除する場合は、子が3個だったら連結成分は常に3個になる。根でないとき、自分と親の関係で混乱した。自分のlowが小さくても自分を削除するので意味がない。結局、自分の子たちそれぞれが親の連結成分とつながれるかという話になる。横断辺がないのでシンプルだ。具体的な計算もけっこう複雑(?)で、変化する連結成分数の合計のほかに元の頂点数と連結成分数が必要。連結成分数は減ることもある。