PAST201912-OPEN

PASTのバチャをようやくやった。全完うれしい(多少はネタバレしていた)。しかし最後はてきとうすぎたしサンプルに助けられることも多かった(今回サンプルは少なかったけどわりと親切だと思った)。詰まることは少なかったがデバッグに時間を取られることが多く、そこで力不足を感じた。天才発想一発みたいな問題がないので結果的に順番通り解いた。

A - 2 倍チェック

3分以上かかってしまった。ABCのA相当だと思っていたので、この重実装はきつかった。数字以外が来たらそこで出力して終わる。

B - 増減管理

これは簡単(実装も重くない)。しかしコーディング量は多い。場合分けを書く。

C - 3 番目

ソートしてa[2]を出力するだけ、と思いきや大きい順だった。a[3]を出力するのなんとなくこわいw

D - 重複検査

ここからABCのC相当。最近のCよりはかなり難しい。

元の整数も書き換える範囲も1からNまでなので楽そう(という確信を得るまでがけっこう時間かかる)。各数が何個あるか数えて0個と2個のやつを取り出した。

E - SNS のログ

Nが小さいから愚直でいい。が、サンプルが合わない。フォローフォローはかなり複雑で、自分をフォローしないようにする必要があった。そして、in-placeでやると新たにフォローした人がフォローしてる人までフォローしてしまい大変なことになるのだった(ここに気づくのに時間がかかった)。自分のフォローを別の配列にコピーして使った。

F - DoubleCamelCase Sort

分割するだけでもそこそこの実装が要る。分割してvector<string>に突っ込んだらとりあえずソートして出力してみるが合わない。なるほど大文字小文字を区別しないのね。簡単ではあるけどいちいち実装させてくるんだよなあ。ここでスピードを出せるだけの実力がない。あとは計算量だが、まあ全体で10万だし、均等に分けたら要素数減るし、均等じゃないときは短いほうに合わせた時間しか比較にかからんし、大丈夫やろと提出。トライ木やstd::set(あ、同じ単語もあるからmulti_setか)も考えたけどさすがにな。ここは不勉強(?)だった。

G - 組分け

ここからABCのD相当。

1人目だけ第1グループに固定して、他をどのグループに入れるか3^N通りを全探索。丁寧に計算量落とそうとしたらどう書けばいいのかわからんなと思っていた。

H - まとめ売り

TL4秒なのは入出力が多いからかな?なんか解けそうではあるけど実装に思いを馳せるとかなり怖い。クエリ1だけなら配列で更新していけばいい。クエリ3が加わると、全体の最小値と、全体からいくつ引いたかを持てば行けそう。「片方でも行けるか?」と思ったが結局両方で。クエリ2でも最小値といくつ引いたかを持って、あとはどのクエリでもそれら全部の更新ができるかという話になってくる。頑張って書き直しながらどうにかサンプルが通ったけど、対称性もクソもないコードになって、一応全部カバーしてるつもりだが正しいという確信がまるでないという状態だった。ACだったけど、もうちょっと確信を持てる形で書く力が欲しかった。

I - 部品調達

これはbitDPで小さいほうからやっていけばいい。が、どうやればいいのか全然わからない。類題は経験があるけど、あのときどうやってたっけ。ダブりはあっていいので、自分より小さい2個に分けるとき分け方がめっちゃ多くなってしまう。まあ、「ちょうどそれにするコスト」ではなく他の部品もあっていいことにすればいいというのは知ってるんだけど、その考え方がどうにも頭の中でまとまらない(正しさを証明しながら進むことができない)。結局、証明せずにそれっぽいのを書いてACをとってしまった。

あと、ビット演算で「iがjを含む」をどう書くか混乱してしまった(得意分野なのに)。この問題も含め、計算量は余裕がある問題が多くて、そこで楽ができた。

(追記)ABC142Eが類題で、当時の自分は2^N個から2つ選ぶのを全部試して配るDPみたいにするのをN回やれば伝播するだろという解き方だった。

J - 地ならし

あと6問。いよいよ本番という感じ。この時点で2時間は経っていない。

ダイクストラ法っぽい。しかし、一度通った道を通るときはノーコストだ。また、区画間の移動ではなく区画を使うことにコストがかかるので、変なことが起きないように気をつける必要がある。さて、入力例1がめちゃくちゃ親切だ。右下隅への行きと帰りで同じ道を通っている。各区画から3つの隅へのコストがわかればいいのではと予想できる。正解ルートを考えてみると、まず(同じ区画を2回通ったりはせずに)右下隅へ行き、その後来た道を戻ってからその後は「既に通った区画を通ることなく」右上隅へ行く、ということでいいか?よさそうだな(ちゃんと考えてなかったけど既に通った区画へはノーコストで行けるので整備しながら進む意味はないね)。あとは2500*2500*log(2500)とかが通るか。さすがになさそうか。ここで、3つの隅からそれぞれダイクストラするだけでいいことに気づいて解決。各区画に侵入するときのコストが3回足されてしまうので2回分引く。サンプルが全然合わなかったが、3つの隅のために3回ループする変数にhを使っていて高さのhと被っていた。

K - 巨大企業

LCAで解けるやつだ。昔書いたやつをコピーして、書き直すつもりで読んでいく。必要な処理を入れて不要な処理を除く。辺は親から子へ向かうのだけ張ればいい。Nが2^17より微妙に大きい。「循環は存在しない」という制約を見て「循環するケースに気づかなかった」と思った。特に難しいところはなかったが、たまにしかやらないことは時間がかかる。ライブラリが抽象化できてない。

L - グラデーション

M本の小さな塔がなければ最小全域木。そこからしばらく考えたが、2^M通り試せばいいのではと気づく。使う小さい塔を固定すれば、使うやつ全部行き来できるようになるので。Nがずいぶん小さいけど、最小全域木を2^M回やるだけだった。何と何をつないでコストいくつというstructを持ち、ソートして小さい順にUnionFindで見ながら追加していく。最初、追加する辺が1本多かったがサンプルは通っていて怖かった。なぜ明らかなバグで出力が変わらないのか少し考えたがわからないので提出してしまった。ここも、よくわからないまま。

M - おまかせ

残り3問、1問40分として2時間残したかったが、ちょっとオーバーしてしまった。

何でソートするんだろうなあという感じ。とりあえず、二分探索ができることに気づく。基準となる値があれば、その基準に対してどれくらい足りないどれくらい埋め合わせが必要かというのがわかりそう。頑張って書くが、まともな値が出てこない。かなり長時間悩んだ。ここで原因の切り分けをしに飛び込んでいけないのはだいぶ弱い。結局、何でソートするかが間違っていた。縦が魔力で横が質量の長方形を横に並べる図を描いてて、それがダメだった(「魔力」と「質量あたりの魔力」を混同した?)。割り算なんだから、食塩水のイメージでよかったんだ。Aが水+食塩、Bが食塩とすれば、強さは食塩濃度になる。BがAより大きいこともあるけど定数倍すれば小さくできるし、だから大きいままでも問題ないだろ。この場合、ターゲット濃度との差*Aでソートすればいい。イメージとしては、横が水+食塩の量で縦で上を水・下を食塩として長方形に区切る。ターゲット濃度の横線より上が余った食塩。足りないところに埋める。埋まったかどうかで二分探索を回す。

N - 整地

かなり難しそうだが、長さC以上の区間はある石の右端から別の石の左端までという現れ方しかしない(門の端も石みたいな扱いで)。しゃくとりだ。しかも、このしゃくとりは面白い。右端の位置でソートした石を加えていき、左端の位置でソートした石を長さC以上の区間ができるまで除いていく。違う並べ替えをしてるのに、ちゃんと追いかけっこが成立している。そういう新しいことを確認しながらだったのでコードは汚いままだったが、サンプルが通ったので提出した。運よく通って最終問に40分以上残せた。

最後のほうにTL4秒の問題あるなあと思ってたけどこれだったのか。Nも10万だし、意図がわからん。

O - 持久戦

これは相当しんどい。考察も実装もしっかりやらないといけない。今回はここで初めて考察コメントを書いた。N*6個の各出目について期待値がわかればよくて、一番大きい目は期待値0。2番目は、1番大きい目を持つサイコロを選べば1/6の確率で大きくなる(最初Aがすべて異なるという制約を見逃していて複雑に考えていた)。3番目に大きい目を考えると複雑になってくる。O(N^2)でいいなら大きい目からDPできそうで、高速化するには次に振るサイコロがわかればなあというところ。ここで、目ではなくサイコロに対して期待値を持つことを思いつく。状況によって小さい目が出たら即終了なので6通りくらいの期待値を持っておく(結局、有効になる目の個数が0から6まで7通りにした)。大きい順に処理するために、目をソートする(何番目のサイコロの何番目に大きい目かの情報を持っておく)(この処理は難しくないが時間のない中これを書く心理ハードルが高かった)。大きい目から処理していってサイコロの有効な(即終了しない)目が増えていくと期待値は改善する一方(0よりは、0より大きい値ほうがいい)なので単に今までの最大値を持っておくだけで最適なサイコロを選べる。現在の目を持つサイコロで有効な目が増える処理をして、サンプルを試しても出力は0。うーん、どこかで1を足すんだろうな。適当に足したら出力例より1小さいのが出てきたので適当に1を足して出力。サンプルが通ったし残り3分しかないので提出、AC。