ABC193

Dまでの4完。久しぶりの水パフォ相当をunratedのときに出してしまった。Dまで遅いのは仕方ない(内容的にも悪くはなかった)。EFは解けそうだったのでTwitterを見ずにやり続けていたが、翌日の昼までかかる。120分でやってほしいセットだなあと思っていたけどかかりすぎやろ。unratedなのでやる気がなかった面はあり、前半は心拍数もずっと落ち着いていた(それでもトイレは直前に行ったけど)。しかしEFに届かないのはとても悔しかった。

A - Discount

定価をベースとして値引き額が何パーセントかだから、まず値引き額を計算して、割合を出し、100を掛ける。1分39秒かかっている。1分は切りたいところだけど、初見だと仕方ないかなあ。printfのコピペも時間かかってるか。

B - Play Snuke

ゲーム機の話になってて制約のとこ見てAPXと並んでいるのでApexに見えた。問題文はなんか「いくつ買えるか」という話に見えてしまうが、単に買えるかどうかだった。買える中で安いやつを選ぶ。こういう無駄に難しいBは苦手感ある。

C - Unexpressed

2^4と4^2などの被りがある。素数だけ考えればいいと勘違いしてサンプル通らず。確かにそれなら被らないけど6^2とか抜けてるんだよなあ(ひどいけど、合成数の冪を扱う機会が少ないとかか?)。まあ、aを全探索する。a^2がN以下なら、a^3はオーバーフローしないのでNを超える判定も大丈夫。std::setで被りを排除(全部llで書いてたのに手癖でset<int>にしてしまい1WA)。O( (√N)*log(N) )に収まってなさそうに思うけど、現実的にはだいぶ少ないはずなので提出。いかにも計算量落ちそうだけどすぐにはわからないね。

D - Poker

問題文を斜め読みして「点数の高い手札」を先に認識してしまい混乱した(書かれている数の最大値を比べるのか?とか)が、1行上に手札の点数の定義が書いてあった。そうなると、81通り全探索できるので簡単。まず、表になっている8枚を除いたカードの枚数を書かれている数毎に数える。そして、裏になっている2枚について全探索し、そうなる確率と高橋君の勝敗を求めて足していく。裏になっているカード2枚に書かれている数がそれぞれa,bである確率は、表になっていないaがp_a枚あるとして、1枚目は p_a / (K * 9 - 8)、2枚目はaとbが異なるなら p_b / (K * 9 - 9)、同じならp_bも1減っている。わりと瞬殺だったが、問題文の確認や実装にかなりの時間がかかった。

E - Oversleeping

順番に解いていく方針だったが、15分くらい考えて時間がかかりそうだったのでFと交互にやった。

最初は何もわからずふわふわしてただけ。だいぶ経って、制約がきついから1000通りくらいの下車チャンスを全探索できると気づいた。そういうズレになる最初の時刻は?という問題だ。extgcdで、電車と睡眠の周期のgcdのズレは実現できる。ただ、どっちにずれてるかはわからない。そこで、電車のほうがはみ出すように固定する(この計算も経験がないからかなり時間かかる)。extgcdの「ax+by=gcd(a,b)」という式で、xが正になるようにするのと、両辺に-1を掛けて同じことをするのの違いとかかなり混乱する。ズレも、どっちのどこを基準に考えるのか、簡単そうなのに全く見えない。解ける問題なのはとっくにわかってるのに自分のコードは全く進まない。わからないまま眠くなってしまったので寝た(コンテスト終了時には、寝る前にEF通してブログ書くくらいまではできるかなとか思ってた)。

街Aで電車を降りてもいいと誤読していた。スタートからして半分ずれているのでまた難しくなる。最小公倍数周期で状況が同じになるのだけど、時刻が非負の最小を求めなければならないのできっちり0で切りたい。でも半分ずれてたら0ってどこ?みたいな。睡眠の周期をベースに考えるよう変更した。extgcdで得た式の両辺に好きな数を掛ければ希望のズレが睡眠周期何回目か実はわかる。それは過去かもしれないが、過去だったら周期の最小公倍数を足せばいい(状況は変わらない)。符号で苦労していたけど最終的にはまとまるんだね。電車の周期で考えていたときの名残のコードでWAを出した(修正してAC)。

(追記)atcoder::crtを使ったら死ぬほど楽だった。

F - Zebraness

いかにもフローという感じ。マス目を頂点とし、BとWに塗り分ける最小カット。B側とW側に分けたときのカットの容量の最小が最大フローでわかるのほんとか?と思いながらグラフを考える。無理そうなので他の解法も考えるがやはりこの問題はフローみがある。ACLのドキュメントを見ても計算量的に間に合う。しばらくして、グリッドだから場所の偶奇で色を反転して考えて違う色であることにペナルティを課す問題と見ることができると気づいた(当時はなんとなくだったけど二部グラフってことね)。

コンテストが終了して、この問題と関係ない小さいグラフを手で作り、ACLのmaxflowで最小カットを求めてみる。あー、SとTに分けて、S側からT側へ向かっている辺ね。逆は関係ない。出口と関係ない頂点を追加してカット容量を増やそうとしてもほんとに増やすと流れちゃうんだ。といった、何を今更というようなことを確認して、改めて形式的に最小カットへ帰着させる気持ちに切り替わった。接するマス目は縦横にあって、そのパターンは3^2で、辺の張り方が全部で18通りもある。そこで、辺を張るのの関数にして縦横をまとめ、2マスの色をソートしてパターンを減らし、??とB?とW?とWBを処理すれば終わり(BBとWWは何もしなくていい)。BとWが隣接するときに、BからWへ辺を張る必要がある。なので??は双方向に辺を張る。

コンテスト終了後1時間以上かかったけど、わりとあっさりAC。最小カットがやはり全然わかってなかった。中身はわかってなくても道具としては使える気がしてたけど全然だったね。最小カットを使う問題と言えばARC085Eだけど、AtCoderだけだとほんとに少なくて今回の問題はありがたかった。またしてもコンテスト中にACできなかったのは非常に残念。自前のDinicとALC両方で提出した。フローはACLでいいな。最小カット(具体的な分け方)もわかるし、インターフェース部分は自作のとほぼ変わらない。

(追記)9通りの場合分けは不要で、全部双方向に辺を張るだけでよかった。「ここは辺を張らないといけない」は考えていたが、「ここに辺を張ってはいけない」を考えた記憶がない。定数倍しか変わらないんだから全部張っていいんだ。やはり不慣れ。