ARC174

4完。パフォーマンス2000ジャストは初。解説放送をDまで聞いて、その後はmomokenの配信を聞いていた。

A - A Multiply

0倍もあって怖い。累積和で、どの2点をとると一番増分が大きくなるかな、というので少し時間を使ってしまったが、前から見て最小値を更新しながら右側を全探索すればいい。最初は0倍が特異点かと思ってたけど、この内容だと1倍が基準なんだね。

今回は、同じことを2回書かないように、Cが1より小さいなら処理の前後で累積和の符号反転をしたけど、けっこう考えることが増えたので、どっちがよかったのか。ただ、1回で済ますメリットは大きい。2回書くとバグのありえる場所が2倍になるというのもあるけど、1回ならバグが顕在化する機会が2倍になるのがすごく大きい。

B - Bought Review

3を基準に貯金がいくつみたいなやつ。借金返済は、4か5でやるしかないが、当然安いほうを使う。ただし、5のほうは2個単位なので半端の1個を4に回すケースも考える(書き上がるくらいのタイミングで気づいた)。

C - Catastrophic Roulette

めちゃくちゃ簡単に見えたが、考えてみると自分にはけっこう大変だった。実際簡単なのだとは思うけど。

それまでに出た整数の個数がKからK+1に増えるまでに、そのときの手番側と反対側にどれだけ罰金が来るかというのを計算する。それとは独立に、それまでに出た整数の個数がKになったとき先攻の手番である確率も求める。これで解けるはず。手番は無限級数でまあ簡単。罰金がなんか難しくて、連立方程式にしたらまずその方程式で合ってるのか自信が持てないし、解こうとしたら二次方程式が出てきて一旦退く。よく考えるとKが増えるまでの手数がわかれば罰金もわかるのだった(最後以外全部罰金、これにここで初めて気づく)。これも無限級数だが、こちらはやや難しい(何も困難はないがΣA^iとΣi*A^iの違いでちょっと段階が増えるだけで負荷がかかってしまう)。かなり頑張って時間もかけて計算した。最初の手番と反対の期待値を求めて、手番の確率を使って答えの変数に足していく。

サンプルなかなか通らなくて、(1 - p) * (1 - p) と 1 - p * p を書き間違えるのを2箇所やってた。先攻の手番になってる確率を出力させたら2になっていた。modintとdoubleを切り替える仕組み用意しといたほうがいいかな。

気にせず割り算してたら673msもかかってごめんなさい。分母がmod 998244353で0になるかを全然意識してなかったの解説見て初めて気づいて、よくない(気づかないほうがタイムはよくなるというのが嫌だね)。

D - Digit vs Square Root

実験してそれを実装するだけ。実装でけっこう時間使ってしまった。[1, N+1)と指定した区間の共通部分の長さを返す関数を作って、条件を満たすO(log(N))個の区間を調べる。実験コードを使って答え合わせするが、普通に間違っている(サンプルは通る)。100倍していくのかと思ったけど、10倍していって (t - 2) * t で 998000 を作ったりするんだ。

完全に実験だけで、あとは順位表でペナがそこまで多くないのを見て提出。Nが大きいときにこの規則性が崩れるのではないかと不安だった(残り時間が減ってくると、その不安よりもとにかくACの可能性があるコードを提出しようになってくる)。解説放送を見ると、なるほど10の冪に近そうだとは思えた。

E - Existence Counting

まず2乗を書く。「tが含まれない」を求めて全体から引く方針。combとpermを間違えていた。next_permutationに0と1を入れてcombにするイメージだったけど普通に間違っていた。それでサンプル1は合ったけどサンプル2が合わずに終了。

(追記)どこが間違っているか、どうしてもわからなかったので、手元でN=8のランダムケースを作り愚直と比較していく。K=2で答えが1だけ違うとかになったのでそこを狙う。そもそもどういう方法でやっているかというと、まずt関係なくP未満の個数を求めておき、そこからtを含まないP未満の個数を引く。辞書順でP未満のものを求めているので、P以下にするためtがPに含まれるなら1を足す。で、どこでずれてるのか条件に合うものを全列挙させたりして見ていくが、なんかもう1ずれる場所が感覚と反対で何をしていいのか全くガイドがない状態でつらかった。結局、Pの各要素について自分より左で自分より小さいものが何個あるかを前計算しておいて引く必要があった。permで求めるところだけでなくP未満を作る(現在の位置での)先頭の選択肢も減るよね気づかなかった。

これで2乗が書けたので高速化するだけ。なんとかできそうだ。前計算の部分は、BITを使えばいい。t毎の計算も、BITにmintを乗せて使う。t(tを含まないものを数えている)がP_iより小さいとき、P未満を作るための選択肢が1個減るので、最初から全部1を引いておいて、tの昇順で処理するときにtと等しいP_iが存在すればその分を足してBIT更新(iを求めるテーブルを作っておく)。P_iとtが一致したら打ち切るのでそこまでの和もBITで求まる。もう何時間かかったかわからないが、素直に考えていけば解ける問題だった。