ABC196

いやーなんとか凌げてよかった。結果としては、FがFFTだったおかげでいい順位。開始50分の段階では、DもEも解けているつもりが通らずFはまだ読んでないという、3完で終わる未来すら見える状態だった。Fを通してDを見直したら即バグを見つけてAC、Eも落ち着いて考えたら見落としがわかった。

A - Difference Max

かなり難しそうな見た目。全探索もよぎる。しかし面白そうな見た目でもあるので、さすがにちゃんと考えたい。最初、x-yの絶対値だと誤読しており、最大と-最小の大きいほうを求めようとした。最大だけでいいので、xはなるべく大きく、yはなるべく小さく、つまりb-cが答え。

B - Round Down

色々考えてしまうが、文字通り切り捨てるだけ。小数点を見つけたらそこ以降を削除(なければそのまま)、そして出力。

なんか久しぶりにいい感じのABだ。好き。

C - Doubled

Cで桁DPとは恐れ入った。じゃなくて制約が答えだね。条件を満たす数を列挙しようとすると変に飛び飛びになって難しそうだが、よく考えると繰り返される数は1から999999までの全ての整数だ。あとは繰り返した数を作るのに一手間必要で、例えば99を2回繰り返した数を得るには99を100倍して99を足せばいいので、桁数に応じた100を用意しておき、99の次に100になったら追い付いたので10倍して1000にする(100*1000+100は100100)。これで全探索できる(Nより大きくなったらループを抜けていたが、ループを抜けず単にN以下のものを数えたほうが明快だった)。

D - Hanjo

1*16のような細長い部屋もありえるけどサンプルに入ってないというABCらしい問題。半畳*16しかないので全探索できそう。しかしC問題には置けないくらい難しいということだ。一応畳を置ける場所は4*4の部屋で24通りもあるので、あんまり雑にやると実行時間が心配になる。DFSなら、毎回全部の畳を置く必要もなく、枝刈りも利くので、大丈夫そう(なうえに実際的にはかなり短い時間で実行できそう)だ。実装は、畳を置ける場所(16未満のマス番号2つ)を列挙して配列に入れ、置かない場合と(置けるなら)置く場合とでDFSしていく。探索深さ(どの場所を使うか)と、これまでに何枚の畳を置いたかを持ちながら。A枚置いたらカウントしてreturn。

サンプルが合わず、5分くらい粘ったが、どう考えても正しいので21:28にあきらめてEへ。Fが解けて戻ってくると、縦の畳でW引いて横の畳で1引くべきところ、Wと1を逆にしていた。Fの提出からわずか1分ちょっとで提出している。

E - Filters

一瞬セグ木系かと思うが、そうじゃなくて上下からこうあれしていくやつだ。合成された関数をfとおくと、fは広義単調増加。高々2回の傾きが変わる場所を二分探索で見つけることも考えたが、かなり大変そうなのでここはやはり正攻法。a_iが足されることでずれるのを累積和で補正する。和のぶんだけ引いてからmaxやminをとれば、最初のxをstd::clamp(x, min_a, max_a)のようにクリップできる。あとは累積和を足すだけで答えになるっぽい(なんか場合分けして足す必要があると思ってたがそんなことなかった)。

提出してWA。7分くらい考えて、どう考えても正しいのであきらめてFへ。FとDを解いて戻ってくる。まだ20分以上あるので落ち着いて。一つ一つ理屈を追っていくと、minとmaxが逆転するケースに気づく。これを知らなかったわけではないが、イメージしづらいので無意識にスルーしてたね。全部持っていってしまう処理だ。minとmaxどっちもmaxとるみたいにする。納得の1ペナ。これで全完。

(追記)最初はf(-INF)とf(INF)を求めようとしてたのに、実装してるうちに最大値と最小値を更新みたいなイメージにすり替わってしまってたなあ。-INFにf_1, f_2, f_3とかましていって、増える一方だと勘違いしてしまった(min操作で普通に減る)。

F - Substring 2

各位置について何文字違うかがわかればいい。いかにも解けそうなので(面白そうなので)その方向で考える。累積和とかロリハとかで、できそうでできない。FFTじゃん。10^6だけど3秒だし自分のFMTはそこそこ速いので実行時間は大丈夫。ここまでCまでの3完しかしておらず、FFTの使い方も思い出せないのでかなり不安になっている。しかし、畳み込み後p[i]に入っているのは添え字の和がiになるペアの積たちの和だと思い出してからはスムーズだった。Tは逆さにして、位置が0からN-Mまでを試し最小値を求める。FFTでわかるのは「どちらも1」の個数だけなので、累積和を使ってそれぞれの1の個数も求める(これで11,10,01,00それぞれの個数がわかる)(累積和なしで直接求まるような上手いやり方もある気がしたが無難にこっちで)。サンプル通ったので提出してAC。20分で通せたのは大きい。

(追記)'0'と'1'を、1と-1にすればいい。同じだったら積は1、違ったら-1なので、和がわかれば何個違ってたか計算できる。