ABC345

ABCDFの5完。久しぶりにABCでいい順位。運も良かった(Dで計算量を確認せずに提出してAC)。Dが重実装(面倒というよりループが深いので書き切るために高い能力が要る)でEも難しく、いいパフォを取るのは半ばあきらめていたが、Fが(おそらくDEが難しいせいで)あまり解かれておらず、結果として早解き(?)で橙パフォ(ABCのカンスト)。

A - Leftrightarrow

"<>"は条件を満たさないけどSの長さは3以上なので気にしなくていい。あとは先頭と末尾と真ん中が全部合ってるか確認すればいい。

B - Integer Division Returns

10で割って切り上げ。X以上で最も小さい10の倍数を得るのは自分のライブラリでできる。それを使えるのか、どう使うのかが最初なかなかわからなかったが、まあ使える。その10の倍数を10で割れば答え。これが本当に正しいのかの証明はすぐにわからなかったが、このしっかりしたサンプルが通っているので大丈夫と判断して提出。まあ10で割った世界だと1の倍数を探すことになり、割る前だと10の倍数を探すことに相当ということで正しいね。

なんとABC239で同様の問題が。問題名まで意識してReturnsを付けている。今回のほうが悩んでしまったな。

C - One Time Swap

ARCのAか?ちょうど1回の操作。元の文字列と同じになるものがあるときとないときがある。操作の位置が違っても結果同じ文字列になることもある。まず、同じ文字が複数含まれているかで場合分け(同じ文字が複数あるときのみ元の文字列と同じものが作れる)。次に、違う2文字を交換するときを考えると、実は元の文字列とその2箇所だけ違った文字列が得られる(操作を区別できる)(いや当たり前なんだけど全然見えてなかった)。つまり解法としては、まず英小文字をカウントしておき、2文字以上のものがあれば答えに1を足す、次に操作の個数(N*(N-1)/2)を足す、最後に足しすぎたぶん(同じ文字を交換したとき)を引く。

D - Tiling

「回転したり裏返したりして置かれていても良い」とあるので警戒したが、タイルはただの長方形だった(裏返しても同じで回転は2通りだけ)。最近はこういうの飛ばさないことが多い。もうちょっと飛ばしたほうがいいというのはけっこう言ってる気がするけど。飛ばしたところであとの問題も典型だからなあって。今回は結果オーライだったけど、やっぱ飛ばしたほうがいいよな。まあ、やる気がないから飛ばさないんだ。あと今回は方針がすぐ見えたし、見えてなかった実装の難しさにやられていたので、普通に考える価値はあるんだよな。コンテスト中だと混乱をどうにかごまかすという動きになってしまい微妙だけど。

タイルの順番と向きを全探索する。7!*2^7でググると100万よりは小さい。そもそも順番とは何かという話だが、条件を満たす置き方があったとき、適切な順番と向きを設定すれば、まだ置いてないマスのうち座標の辞書順が最小のものにタイルの左上隅が来るように置くことを繰り返して置けなくなったらやめることでその置き方になるので、順番と向きを全探索すればよい。順番と向きを決めたとき、まだ置いてない場所を探索し(合計でO(H*W))、置けるか判定して(ここの計算量があやしい)置けるなら置く(合計でO(H*W))。あやしいところを除くとO(N!*2^N*H*W)で間に合う。サンプルが合わない(この時点で時刻は21:35)のでEへ。

Fを通して戻ってくると、そもそも順列を使ってなかった。直してサンプルは合ったので、少し迷ったけど提出してAC。結局計算量はあやしいままだった。今考えて、置けないときは、その順番と向きを調べるのはそこで終わりなんだから、結局O(H*W)には収まっているか。ほんとか?breakでどこに抜けるのかとか全然把握できなかったんだよな。これ素早くきれいに書ける人はほんとに実力がある。

(追記)再帰で書き直した。再帰のが見やすいコードになる。まだ置いてない場所を探すのは関数の最初に1回。置けたら再帰。whileやifが深くなって混乱してたのが解消された。ただし、置いたのを戻す処理が必要になる。置いてない場所を探してマス目の外に出たら常にYesなの気づいてなかった。また、こういう全探索をするときNは小さいので、どのタイルが残っているかをビットで持てばいいと気づいた。countr_zeroで位置がわかり、l &= l - 1 で消せる。

E - Colorful Subsequence

連続する同じ色の中からは最大価値を1個選ぶ(結局DPするならこれは必要ないかな)。そこまでにK個を超えて消費したら-1。そうでないとき、残った個数を消費するために、同じ色が隣り合わないように除いていく必要がある。難しい。DP。トイレ行ってそこで考えが整理されたけど、結局K^2が掛かってくる。Fへ。

FとDを通して戻ってくる。TL5秒だしセグ木使えないかなと考えていると、セグ木の長さはK+1とかでいいのでlog(K)も小さめではありワンチャン間に合いそう。自分と同じ色のところだけ都度変更しても全体ではO(N)回になるかな。まあそれをK回やってlogもかかるので重いけど。んでセグ木も回数が違うものの最小値(失う価値の最小値)を得る必要があるから、なんかもうずらしてずらして実装したくないしほんとに実現できるのかっていう。

(追記)2つ記録しておけばいいというのを聞いて、その通りにやる。ずっと、最後に置いたボールの位置を状態としていたが、解説を見たら単に現在の位置で、確かにそう考えればデータ構造要らない。じゃあ色の条件を除いた普通のDPを思い浮かべて書いていくかと思ったが、それ(自分にとって簡単であるはず)もすぐ出てこなくて、こういうDPに興味がないのかなと思った。普通のDPと比べて、ボールを置く処理(そのボールの色しかなくなり2位が-INFになる)と、いいとこ取りの合成が必要になる。2位は必ず1位と違う色であるというのが重要。頑張って書くとサンプルが通る。削除数の降順にやればin-placeでできるのでそのように書き直して提出、WA。色c価値vのボールを置くとき、色が同じ場合にvを足してなかった。三項演算子の優先順位がわかってない人みたいになったけど、まあ負荷が高いとこういうことも起こる。

F - Many Lamps

なんか最大マッチングとか調べてたけど、よく見ると操作回数には余裕がある。ていうか同じ辺の操作を2回以上する意味はない(回数の偶奇のみが結果に影響する)から、実質的に回数制限はない。操作で変化するついてるランプの個数は偶数なのでKが奇数はNo。グラフが連結とは書いてないのが怖い。まずグラフが連結のときを考える。全域木をとって根付き木として葉から決めていけば、根以外は希望の状態にできるし、全体でついてるのが偶数という条件を満たしているなら根も希望の状態になっているはず。実際にはKという個数だけが与えられていて自由度が高いのでけっこう混乱した。まあ個数が残っているかぎり葉からつけていけばいい(葉側でさぼるとあとから取り返せない)。実装は、DFS木を使う。子孫の探索が終わりKが残ってるのに自分がついてないなら親にそれを報告し操作してもらう(この実装がパッとは見えなくて、よくない)。連結成分毎に偶数の最大を取っていく感じ。辺を出力してサンプルとは異なる答えを軽く確認して提出、AC。