1ペナ6完。Cまでに22分かかっててゴミ。本来、自分にとって簡単な問題を解くのはつまらないけど簡単なら瞬殺できるからまあOKということのはずが、自分の頭の回転が遅すぎてつまらないうえに時間がかかるという惨状になっている。FのDPをちゃんと書き切ったのはえらい。開始時にGitHub Copilotがオンになっていて、まあ使ってもいいんだけど不慣れなのでさすがにオフった(時間のロス)。これ何が問題って、Copilotを試すために何か競プロの問題を解こうとして面倒で1問も解かないまま今日を迎えたんだよね。競プロに対するやる気がめちゃくちゃ落ちている。飽きたと言ってもいい。緑から青diffの問題は面白いけど、そこから外れた問題に魅力を感じなくなっている。難しい問題については、2時間あって自力ACできない問題は解説を見ても自分の能力を超越していて断絶を感じて無理になる(楽に勉強できる典型はほとんど勉強してあるから)。
A - Full House 2
カードを1枚ずつ追加していくとき、カードの枚数が3,2になるためには3,1か2,2を経由する必要があると思う(未証明)(A問題でここまで高い考察難度やめろ(嫌なら全探索すればよかっただろ))。枚数をカウントしてソートしてそれを確認するコードを書く。ソートを降順にするのを忘れてサンプル合わなかった。カード追加を13通り全探索というのもあるが、はっきりしないのでスルーした。今見ると、0が書かれたカードを追加するのも許されそうで、問題文がやや難しい。
解説のおまけのところ、サンプルをよく見てなくて気づかなかった。こういうのいいね。
B - Calculator
AtCoderでこういうのを見ると「DPか!?」って思ってしまうが、よく見ると貪欲でいい。ループ回数が|S|-1でいいのかなり意外で動揺した。
C - Operate 1
Fを読んだが、すぐには解けなそうなのでまずはCを倒す。文字数の差で場合分けする。変更は簡単。挿入は、同じ位置で違う文字があったらそこにその相手の文字を挿入してあとは愚直比較。削除は挿入の文字列を入れ替えればいい。cin >> s, t; を久々にやらかして時間を消費した。
提出してWA。Sの文字数をNとして、挿入できる場所はN個ではなくN+1個だった。N文字一致したら末尾にTの最後の文字を追加するようにしてAC。解説にあるように、前後から一致する文字数の最大を求めるのが明快だった。
D - Diagonal Separation
全ての(すでに塗られた)黒マスについて、自分を含む左上の長方形領域に白マスがなければ、Yes(その長方形の和集合だけを黒に塗ればいい)、一つでもあればNo(その白マスから右と下に動いて黒マスに行ける)。縦位置でソートしてBITかなあと思い貼ってしまったけど、よく考えると(白マスの)横位置の最小値だけでいい(最初最大値で実装しててサンプル合わなかった)。ソートは、同じ行なら白マスを優先する(列はどうでもいい)。ソートした順に見て、白マスなら最小値を更新、黒マスなら自分と同じか左に白マスがないかチェック(自分と同じか上の白マスだけ見ていることに注意)。
E - Maximize XOR
最初は嫌だなあという印象だった。上位ビットから決めようとすると、その位置で何個の1を選ぶかが色々ありえる。制約の意図がわからん。next_permutationの計算量を調べるが、だめかなあという感じ。じゃあDFSは?ワンチャンO(comb(N, K)+N)とかで行けたりしない?と思ってとりあえず実装してみる。Kが大きいときはN-Kに置き換えてあとでA全体の総xorとxorをとる。ラムダ式による再帰も慣れたもんだ。選ぶ回数が残っているときだけ選ぶのは当然だが、選ぶ回数と残り深さが同じときは「選ばない」という選択ができないことにも注意。サンプルは通ったが、計算量がわからない。DFS木が、最後に枝分かれしてからも長く続いているとよくない(極端な話、深さ1とか2とかで全部分かれてしまったら全体にNが掛かってしまう)。実験すればいいと気づきやってみると、Nが小さいときは大丈夫そうだ。N=200000, K=1みたいのがやばい。一方、N=1500, K=2はなんとかいける(制約はギリギリ満たしていない)。長い一本道を次の枝分かれまでスキップするようなことも考えたが、K=1(K=N-1も含む)だけ場合分けすれば行けそうなのでそうした。K=1はN通り試すだけなので簡単。サンプルが通らなかったとき、2024が正解のところ2025と出力していて、芸術点が高かった。279msでAC。
スキップする方法でAC。何回連続で「選ばない」をしてから「選ぶ」かでforを回す。長い一本道はなくなり、どの葉から見ても探索深さがO(K)なのでOK(アクシデンタルダジャレ)。何回連続で「選ばない」ができるかの計算が難しく、1WAを出した。更に解説を見ると、元の自分のコード(K=1を場合分けしないとTLEすると思われる)に1個条件を足すだけで、スキップする方法と同じくらいの実行時間でAC。単に残り回数が0になったらそこで最大値更新してreturnすればよかった。途中の長い一本道はいいのか気になったが、よく考えるとそこは毎回分岐していてforと同じような処理になっている。分岐しないのは残りを全部選ぶときだけで、Kが小さければ問題ないし、Kが大きければ元のコードですら速い。毎回(2個以上に)分岐するなら、そもそも分岐は葉より少ないのでDFSの呼び出し回数もその程度で済む。
F - Operate K
Cをやってたときは解像度が低かったけど、改めて見るとまんま編集距離なんだよな。DPテーブルを横に見て(Yesの場合は)値がO(K)回しか切り替わらないとかそういうのだろと思って、まずは計算量を考えないただの編集距離を書く。このDPを思い出してくると、そういうんじゃなくて編集距離がK以下の部分だけやるみたいなのだとわかる。もう少し考えると、文字数の差がKより大きい場合はNoなので、単に文字数の差がK以下の領域だけをやればいいとわかる。DPテーブルは2行分持ってswap(これはO(1)でできる)しながら使う。問題は、前の行で文字数の差がK以下でない領域を参照して遷移する場合もあるということ。場合分けは面倒なので、有効な領域の前後1個ずつをK+1で埋めることにした。s[i]を追加してi+1文字になる(DPの状態にはi+1を使う)というややこしさがあり、K+1で埋める処理を間違えて時間がかかった。編集距離のDPが、dp[i][j]でSの先頭i文字とTの先頭j文字の編集距離を表しているとわかっているなら、あとは必要な部分だけ更新すればいいという簡単な問題。が、その実装がけっこう難しくて、そこが面白かった。