PAST202203-OPEN

珍しく全完。Nが怪しい。

A - 3枚のカード/Three cards

ソートしようと思ったら負の数もあって残念(?)。全探索。あー、ベタ書きもありだったね。

B - 花束/Flowers

赤い花だけから成る花束みたいのがないのが親切。割り算(切り捨て)して小さいほう。

C - Go Further

もう単項式という言葉を使わずに定義していいだろ。制約の81がよくわからなかったが、(26+1)*3なのね。割り算してもいいけど、3より大きい間3を引き続けるのが楽('a'-1から始めて一緒にインクリメント)。数字は先頭の1~3文字を出力。

D - ハイスコア/High Score

更新しながら足していく。簡単で好き。

E - 良い日付/Only 2 kinds

サンプル4で示されている通り、答えとして現れる最も未来の日付は3000/03/03。1000年しかないので思考停止で全探索が可能。でも月によって何日まであるか違うし「飛ばしてもいいですか?」って言って次の問題開いた。けどまあPASTだし戻ってきた。こういう理不尽があるのがPASTだよな。で、書こうとしてうるう年の存在に気づきさすがに手を止めた。今自分がやろうとしているのは少なくとも想定解じゃない。落ち着いて考える。まず、答えが3000年になるものを場合分けする。すると、必ず2が含まれているので他にもう1種類の数字しか使えない。32月とかはないので、0か1しか使えない。するとりえる月や日は手で列挙できる程度しかない。頑張って書く。サンプル通らず、年を含めると2種類に収まっていないケースが当然あった。普通に種類数をカウントしてミスがないことを祈りながら提出、sprintfの速度も心配だったが大丈夫だった。

あとから思えば、毎月22日までしかないとして全探索すればよかった。解説見たらPythonの機能使ってて草。

F - 地図の塗り分け/Coloring map

州が異なりかつ色が同じものがないかチェックする。

G - 方程式/Equations

端(1や2)が答えのことないの親切~(と思って解いてたけど1や2が解でないという誤った仮定で解いていたよ酷い(この問題の制約下ではたまたま正しかった))。左辺が連続で区間内に解が1個しかないということはそこで符号が変わる(と思ったけど符号が変わらないケース(x-1.5)^2=0とか普通にあるやん(この問題の制約下ではたまたま必ず符号が変わる))。ということで左辺をf(x)とするとf(1)と符号が同じかで二分探索できる(制約から単調増加なの気づいてなかった)。

なんだこれ酷すぎる。ちゃんと解いたつもりだったのに。

H - 連結成分 / Connected Components

HとIを読んでトイレで考えていたとき、ACLatcoder::dsuには頂点番号のリストを返す関数あったなあとか思ってたけど全体を返すのだったそりゃそうか。最初はUnionFind貼るだけやんと思っていたものの、グラフを構築する方針に切り替えた。ここでもUnionFindは役に立ち、グラフが森を保つように必要最低限の辺を追加していく。クエリ毎にDFSするけど、連結成分が木だとDFSが書きやすいし、全部の辺を探索したらTLEになりそうなのでそれも避けれている。

解説見たらマージテクじゃん。いや自分の解法かなりきれいだと思ったけどな。

I - 対称変換/Symmetric Transformation

急いで読んでトイレに行ったこともあり、どちらも何度でも操作できると誤読。その場合は、90度回転などはできないが、好きな位置に縦横反転させるか4通りの好きな向きで置ける。4通りについて対応する点との位置の差が全部同じであるかチェックすればよい。対応する点を見つけるには、座標に適当な順序を入れてソート、左右反転はx座標に-1を掛ければいい。ここから誤読に気づいて軌道修正は精神的につらかった(追加の実装量はそれほどでもない)。操作しない場合、差はxもyも0である必要がある。左右反転するときは、x方向だけ好きにできるのでy座標の差が0である必要がある。

J - Expected Range/区間の期待値

ソートしてしまっていい。最大値と最小値を固定すると何通りあるかわかる。2乗なら解けるなあとか思っていると、期待値の線形性がなんか出てきて、最大値の寄与から最小値の寄与を引くだけに。正直よくわかってないんだけど。

K - 旅行計画/Planning

ダイクストラ2回やるだけ。Graph構造体を使っていると、こういうときインスタンスを2つ作ればいいのでよい。

L - N mod M

いつものだけど、意外と厄介。Mが10の倍数とか9の倍数とか。結局、少し前のABCでやったように、行列累乗を使った。上からやっていけば、まず10^D_iを掛けて、11111 mod Mを計算してC_i倍したものを足していくだけ。

解説、これも繰り返し2乗法みたいにできるんだ。なるほどー!そして、9の倍数を9の倍数で割ったあまりは9の倍数だから、mod M*9で10^D_i-1を計算して9で割ってもいいと(なんだこれ感覚としては全くわからないな)。

M - ランキング/Ranking

平衡二分探索木が必要そうなときは大抵トライ木でできる。ライブラリ持ってるのに細かいところでだいぶ時間食ったけど。けっこうサンプルに頼ってしまった。

解説を見て。ひええ、座標圧縮は思いつかなかったなあ。

(追記)同じ値もないし、座圧すると楽になるね。逆引きもただのvectorでいいし。クエリ2はBITで自分より得点が低い人数求めればいいし、クエリ3はBIT上で二分探索。ただ、実装に時間はかかる。

N - 400億マス計算 / 40B of calculations

サンプルにあるように、対角線上に0が並び、そこを対称に符号が逆の数が並ぶ。ソートしてよくて、結局は差の絶対値として何通りあるかわかればいい。これstd::bitsetを使えば計算回数10^9は行かないしいけるのでは?なんか917msで通った。愚直書くだけなのに時間かかった。

しばらく考えていたけど景色が変わらないので解説見た。FFTはちょっと思ったけどわかんなかったな。個数をカウントするのか。言われてみればそうだけど、感覚的にはわかってなかったという弱みが出たな。

O - 3-順列/3-Permutation

図を描いてみるとグラフだ。また、順列は3で割ったあまりで考えていいので0,1,2の個数をカウントしておく。1個決めると連結成分内が一意に決まってしまう。全部0にするか、連結成分が二部グラフなら1と2で埋めることもできる。3乗のDPっぽいが、よく考えると1と2は高々334個しかない(bitsetもちょっと考えたがそのためには普通に書いて感じをつかむ必要があり、そうなると気づく)。遷移の回数が10^8強なら間に合いそうだ。実装していくと、ありがたいことに奇閉路がある連結成分はスルーしていい(1の個数と2の個数を添え字にしてこれまでの連結成分を使って可能かどうかを持っている)。後ろからやればin-placeに更新できる。遷移は1が多い(厳密には「少なくない」)ときと2が多いときの2通り。