ABC312

5完。昨日の夕方に昼寝をしたとき、高温の中長時間眠ってしまい、しばらくしてから微熱が出た(普段涼しいのはエアコンの風が当たる位置にいるからで、昼寝してた位置でも夜は涼しいのは西日が当たらないから)。それがずっと残ってしまった(2日連続で風呂に入らないレベルで悪かった)。とはいえABCが始まる時間にはそれなりに調子が戻っていたと思う。それなりなので、元々の頭の悪さも相まって酷い内容に。

A - Chord

規則性がありそうですぐにはわからないので、手動で "ACE", "BDF", "CEG", "DFA", "EGB", "FAC", "GBD" のように整形する。この整形を高速に行うことがこの問題の本質。区切り文字が全角なの何の嫌がらせだよ。

解説で "ACEGBDFAC" の部分文字列かで判定するのも書いてあるけど、悪い意味でやばい。

(追記)英語問題文からコピペすれば半角カンマ区切りというのはコンテスト後に思いついたけど、それもandが入っているという罠には気づかなかった。日本語問題文からでもそのままコピペしてその文字列からfindすればいいというのは上手いやり方だが、アドホックな感じがするのでこれは思いつかなくてもいいかな。

B - TaK Code

言われた通りにやるだけだが、かなりめんどくさい。

ABで10分以上かかった。ABCって実質90分のコンテストらしいです。

(追記)「TaK Code は以下のものです」って書いてあるの知ってたのにそれをコピペして使うことを思いつかなかったなあ。規則的な形ではあるからなあ。4*4塗り潰してから3*3塗り潰す形なので、それを自分で構築してから比較という方向で考えていれば思いつけたかも。そういう方向へもうちょっと行くべきだったかも。

C - Invisible Hand

需要と供給だ。条件を逆にして最大の整数を求める問題とどう違うのかが全然わからず恐怖した。しかし、言われた通りに二分探索すれば問題自体は解けると気づき、実装。提出してWAで、「えっ」となったが、そうかNとMが異なることを最初に少し気にしたけど、ここで効いてくるのかあ。10^9円で全員売ってもよくて買ってもよいとき、Nが小さいと条件を満たさない(10^9+1円なら買う人はいない)。

(追記)かなり計算量落ちるよね。ソートが支配的でO(N*log(N))から落ちることはないけど。ツイートを見て初めて気づいたけど、売り手がみんな100円以上、買い手がみんな10円以下を望んでいたら、11円で需要と供給が0で釣り合ってしまう。こういうのを想像できずに解けた気になってるんだよなあ。別に何か違うタイプのケースというわけではないけど。

D - Count Bracket Sequences

楽にDPできそう。なんで3000なのかと思ったら、そういうことね。ほんとは1500上限でいいけど、Nを使って表すのがやや面倒なのでNを上限に。残ってる開き括弧の個数は0以上N以下なのでこれでDP。さすがにこれは簡単。

E - Tangency of Cuboids

制約から100万個の立方体に色を塗れるので解けそう。共通部分の体積が0なので計算量も大丈夫。読み方が雑で、面積の合計を求めてしまった(全ての境目に対して色が異なるか判定)。直方体毎にいくつの直方体と接しているかを答える問題だった。やや重くなるが、setで色の組み合わせの重複を排除してカウント。

わりと簡単だと思ったのに全然解かれてなくて驚く。

F - Cans and Openers

少し考えて、嫌な予感がしてGへ。缶切りが要らないのを大きい順に見て、缶切りが要るのを長く使える缶切りの分だけ混ぜ込む。計算量の落とし方が見えにくい。

コンテスト後に考えると、普通にmultisetでできた。実装していくと、priority_queueでよかった。缶切りが要らないやつがM個ないことがあり、缶切りが要るやつが尽きることもあるし、缶切りがM個以上あることもある。解法のイメージは簡単そうに見えて境界が相当面倒で、思ったよりずっと時間と体力を使う問題だった。WAを出したのは、少し考えてwhileをifにしたのが普通に間違っていた(whileが無難だったけどなんか不要だと思ってしまった)。

G - Avoid Straight Line

FGの自力ACに2時間かかってもう2時なので続きは明日書く。

(追記)順番も区別してもとめて1/6すればよさそう。単純パスが存在するものを数える方針でいけそう。使う頂点を1個固定すると、残りの2つが同じ子の子孫のときと別の子の子孫のときがあり、全方位木DPならできそう(全方位木DPが本当に必要かの判定がいつもできない)。頂点数と、根からバックせずに取れる2つの頂点の組の個数を持てば、答えが計算できる。しかしサンプルが全く合わない。2個組は順番を入れ替えたものをカウントしてないので、6倍ではなく3倍にしかなってなかった。全方位木DPするの久しぶりなので、自分のライブラリがどういう処理をしているかの確認に多くの時間を使った。バグの原因が何もわからない状態が続き、終了間際になって部分木の答えが余分に足されていそうだと気づきようやくデバッグらしいことが始まるが遅すぎる。FGのうちGを通すことは前提で考えていたからずっとGをやっていた。自分の実力を高く見積もりすぎている。

終了後もやって、1時間以上かかった。おかげで全方位木DPの復習にはなったかな。サンプルが通らない原因が全く分からないし、めちゃくちゃイライラしながらやってた。深夜になり大きな声が出せないのもきつかった。その木の答えを求めるときに、合計を求めたら答え用の変数にコピーして合計は0にするとよかった。致命的だったのが、結合法則を満たさない演算にしていたこと。間違ったイメージを元にした酷いコードが生成されていた。

解説をいくつか見た。パスの真ん中に来る頂点を固定して考えればこれは違う子の子孫から1個ずつ取ってくるだけでDFS1回で計算できる。こんな簡単だったのかあ。

Ex - snukesnuke

(追記)翌日になってから考えてみたが、実装途中で今の方針ではできないと気づき解説を見た。簡単そうに見えるのでまず愚直を書いてみた。この計算量がまたわからない(これを曖昧なままスルーしてしまうのが今の自分の弱さだ)。2乗か3乗になりそうで、ちょっとここからは方針が見えない。そこで、現れる文字列を全部最も小さい繰り返し単位(これの一意性みたいのが気になったが、倍数でない(長さ10に対して8みたいな)周期があったとすると結局それらの最大公約数の短い周期が存在することになって矛盾しそう)に分解してみる。これは約数の個数分チェックすればいいので間に合う(解説を見るとZ-algorithmで高速にできた)。unordered_mapを使ってその繰り返し単位毎に処理する。ここでmexみたいな処理が必要になり、setでできる気がしてたけどそうでもなかった。S_iが何回繰り返したものであるかが色々なので大変そうだと思っていたが、解説を見ると何回繰り返したか毎にどこまで調べたかを保存しておけばよかった。