AGC029

反省点というのは体調管理(コンテスト中は、動きも立ち回りもよかった)。朝から微妙な感じだったのに、熱を測ったり休んだりといったことをしなかった。体調が悪いからこそ、気力がなくて休むこともサボってしまうというのはあるけどな。どうも頭がすっきりせず、直前に熱を測ると37.0。最近調子よかったから油断してたよ。なんでAGCのときに。体はべつにつらくなかったので、見ないふりをしてAGCに突っ込んだ(参加しないと異質の悔しさに襲われる)。頭が回らないので、遅解きで点数を稼ぐしかない。いかにもプロコンらしいC問題が、解ける状態じゃなくてね。体調が悪いときのコンテストというのは、とにかく楽しくない。苦しんで解き、ACを取っても少し安堵するだけ。ABDの3完で結果だけをもぎ取っていった(それによって精神の安らぎは得られる)。コンテストが終わって布団の中で測ると36.6だった。

A - Irreversible operation

右端の図を描いて考えていたところ、「これはswapじゃない、白石は存在せず黒石を移動させてるんだ」と気づいた。こんな天才は俺だけだろうと思い、急いでコードを書く。焦って(300早解きは早めに人権が得られて良い)ちょっと足がもつれるが、なんとか書き上げてAC。あとで順位表を見ると天才だらけだった(そういう意味でいい問題だと思う)。

B - Powers of two

Aの制約から、使える2冪は高々30種類しかない。2冪を一つずつ固定して考えていくか?いや、1,3,13,19みたいなとき3と13をペアにしたら1と19が余ってしまう。なんか最大マッチングぽいな。だが、計算量は?マッチングをよく理解してないね。計算量的には無理。経験がないからワンチャン行けるかどうかわからないんだよね。まあAtCoder的に想定解じゃないことは予測できるので、ちゃんと考える。最大マッチングだとしても何かいい性質があるはず。

さて、2冪を一つ固定すると、100000=11010+101みたいになる。少なくとも片方は100000の一つ下のビットが立っている。ただまあ、101も10とペアになれるんで、相手が決まるわけじゃない。もう少し考えて、ペアの大きいほうになることが確定していれば相手は一意に決まることに気づいた。グラフでいうと木になっている。木の最大マッチングだ。木は二部グラフの一種。二部グラフの最大マッチングは有名だから、木の場合とか有名に決まっているのでは?なのに自分は何も知らないし、ググってもよくわからない(そんなことある??)。まあ直接考えることもできそうで、葉ノードはその親とつながるしかないので(そういう性質から木だとわかったんだけど)適当に親とつなげてしまってよい(そうしないメリットは親がそのまた親とつながれることだけだが、同じペア数でより広いスペースを使っているのでデメリットしかない)。つまり、残ってる中で大きい数から順に見てその親と貪欲につなげていけばいい。しかし、木の辺を双方向で持つかとか色々あってかなり混乱した。O(N)では行けそうだけど、具体的に見えない。時間がどんどん溶けていく。ていうか木じゃなくて単に大きいほうから見ていけばいいじゃん。ソートして、二分探索で相手を見つける。mapを使う手もあるけどやはりいつも通りがいい。と思ったけど頭が回らないのでmap(じゃあ普段からmapがいいのでは?とも思ったけどそういう難しいことを考える余裕はない)。mapの使い方がいまいち確信が持てなくて、普段からやっとくんだったと思う。たとえmapでもlogはlogなので定数倍は気にしない!mapをどう使うか悩んでいた(当時は気づいてなかったがmapかsetかという話だ)が、同じ数が複数あるケースがあるので残ってる個数を入れる。あれ、大きい数が横入りしてくることない?(11111と1みたいに)って途中で思ったけど、ちゃんと順序が入ってるから大丈夫(そんなに自信はなかったが)。1が1とペアになることに気づき、1同士でペアを作る処理を追加。サンプル合わず、1000+1000=10000みたいなケースを見落としていた。まさに注意書きの「2番目のボール同士をペアにできないことに注意してください」じゃん!条件を追加してなんとかAC。

C - Lexicographic constraints

「無限に多くの文字があり」というところで、それってwell-definedなのかなN種類の文字でよかったんじゃないかなとか余計なことを考える。サンプルで2種類で済ます方法が書いてなかったので、そこにヒントがあるんじゃないかと自分で構成する。aaa,ab,b。雰囲気は出てる。文字数が増えていくなら、後ろにaを追加していくだけでよさそう。いや、逆から見て、減っていくなら文字を削っていくだけでいい、か?まあそのへんはあとで。Nが20万なので、Aが20以上なら同じ文字数のだけがいくらあっても2種類の文字で足りてしまう。また、1種類で済むかどうかは、狭義単調増加かですぐわかる。細かいところはおいといて、Aが20までと考えていいなら、なんか20桁のmax(A)進法を管理する感じで行けないかな。たとえば"cb"が現れたなら、その先に現れる5文字は"cbaaa"以上でないといけない。そういうふうに更新していく。二分探索も早い時期に思いついてた。使える文字数が固定されるなら少し簡単になりそう。むしろ、問題を簡単なものに分割できるならどんどんするべき。困難はこれからいくらでも出てくる。ただ、どの方針も頭がゴチャゴチャしてしまいなかなか先に進めなかった。かなり時間を使ったが、順位表を見てDへ。

D - Grid game

大して広くもない(メモリには収まらないけど)フィールドにいくつか障害物がある。高橋君は右に、青木君は下にだけ動かせる。パスもできるがパス自体も行動(高橋君の行動回数を答える問題)。連続パスで終局(囲碁みたいだなと思った)なので、高橋君がパスすると、行動回数を少なくしたい青木君は必ずパスし終局させる。よって高橋君は基本的にパスしない。青木君が(障害物があるなどでやむを得ない場合を除き)動くか動かないかを選ぶ(そして高橋君を障害物にぶつける)だけのゲームだ。

異様に簡単そうだが、800点なのでなんかあるだろう。まだ全然具体的な方針は見えないのでもう少し突っ込む。高橋君が毎回右に移動してるので、状況は単純。対角線より下には行けない。対角線上に障害物があったら青木君はそのマスに行けないので、そこから先は対角線より少し上にしか行けなくなる。なんか障害物があればそれに当てたいんだけど、そこに行ける保証はないので困る。いろんな障害物の配置を試して、紙の上で経験値を得る。たとえば、障害物が別の障害物に囲まれて到達できないとき、内側の障害物の左に必ず外側のがあるので、より青木君にとって優れた外側の障害物にぶつければいい。とりあえず可能な中で一番下に到達できるルートを求めるか。これ貪欲でいいか?ある横位置で上にいたほうがのちのち有利になるなんてことある?障害物があって進めないんならそもそもそこでゲームは終わる。つまり、下に行けるときは毎回下に行くことで移動可能な一番低いラインが得られる(もう時間もないし点数だけ得ようという魂胆なので考察は意識できる程度に雑)。もちろん、途中で動けなくなったらそこから先は考える必要がない。ここで、フィールドに端があることを思い出す。横だけじゃなく下も。大した修正は要らなさそう。さて、そのラインより上で最も左の障害物にぶつけることが実は可能なんじゃないかと仮説を立てる。そういう雰囲気がある。だって、防ぎようがないよね。より左に障害物がないんだから。ということで実装に入る。

実装するにあたって具体的な問題文を確認する必要がある。すると、今まで右に動いてると思っていた高橋君が下に動いていたあ~(実際に動いてるのは2人共通の駒だけど、脳内ではそう考えてる)。これはかなり厄介。実装をどちらの向きで行うかかなり迷う。ただ、hとiとxを結び付けるコーディングはAtCoderで慣れているので、きれいに書けばなんとかなるんじゃないかな。障害物の位置はsetで管理する。そのまんま書くだけ(実際には、細部のない考察を最低限埋めながら時間をかけて書いている)。遠い場所はラインの情報が入ってないことに気をつけながら書く(実際は遠いから値を更新しようがなく気をつける必要なかったっぽ)。けっこう混乱する処理で、更新する値を障害物の場所にするかそれとも高橋君が止まった場所かでかなり迷った(どちらでもいいけど、どちらが自然かという話)。サンプルにも助けられながら、合ったらわりとすぐ提出。ACで「よかったー」と思った。

ABC115

A - Christmas Eve Eve Eve

Dの範囲が狭い。if文で書けるようにした問題だが、これは" Eve"を付け足すだけでいい。けっこう速く書けたつもりだが、やはり文字列の処理は時間がかかるようで、1分を少しオーバーした。

後続の問題名を見る。ネーミングが適当すぎないかw

B - Christmas Eve Eve

これは簡単。いつものBより簡単だが、Bらしさはあまり感じない(強いて言えばCっぽい)。一番高いやつに半額を適用するだけ。実際に素早く書けた。

今気づいたが、問題の設定がちゃんと問題名と同じになってるんだねえ。

C - Christmas Eve

見るからに簡単そうだが、すぐには浮かばない。連続したK本とかの制約はないみたいなので、ソートする。あとはループ回して最小を求めるだけ。ループ回数がN-K+1で、+1がどこから来たかわからず不安になる。まあたまに見る形ではあったが、当時はなぜかわからなかった。さて、最大値と最小値を知る必要がある。んー?しゃくとりみたいにしてもダメか。まあセグ木ならできるけど、もう少し考えよう。ソートしてあるじゃん。これは酷い。なぜソートできたかも忘れている。帰着された問題を次のステップへ丸投げしてるからね(短期記憶なさすぎだろ)。サンプルが合ったので即提出(終了が10分遅くなったこともあってなんか焦ってる)。

D - Christmas

やっぱり今回もルンルンだね。問題文の言いたいことはすぐにわかる。Nが小さいので64bit整数でいける。だが解くのは骨だ。サンプル1を手で確認してから考察に入る。まず、レベルLバーガーの層数とパティの数を求める。2倍して3を足すとかでいける。Nが小さいのでまずそれを全部求める。B(L-1)P(L-1)Bみたいな形。んー、木を根から辿るように、自分が今どこにいるかを確認していけばよさそう。ただ、それで解けることはわかっても、具現化するのが自分の力ではかなり厳しそう。もう少し考える。再帰でどうだ。Nの一つ小さな問題が解けてるなら解けるのでは?解けそうだ。変数をグローバルに移動させる。ラムダ式再帰も知ってるし最近書いたし便利なんだけど、よどみなく書けるほどには覚えてないので普通にグローバル関数で。けっこう冗長気味に書くチキン戦法(それが無難なのかは知らない)。けっこう区切りが多いから境界や実装量で困ってたけど、1段分だけやればいい再帰は素晴らしい。前半部分を足し忘れてサンプル蹴られる。直して合ったらやはり即提出だ。

終わってみれば簡単回(自分にとって簡単だったかはともかく)。基本を確認できるいい問題だった。

ABC114

A - 753

なんかうまい書き方があるか一瞬考えたけど、単に書いて時間はかからないのでそれで。

B - 754

またルンルンが出てくる。あのときのwriter誰だっけ?

やるだけ。文字列を数値にする方法を思い出そうとかと一瞬思ったが、単に書けばスピードが出せる。ここはいい動きができた。

C - 755

ちいさな計算量でできる方法もありそうだが、3^9でGoogle検索して19683と小さいので、10^9未満(10^9は七五三数でないため)の七五三数を全列挙する方針にした。桁数で分けて、k桁なら3^k(この計算はpowを使った)回ループして、k桁の数を構成しvectorにpush_back。forのネストが何個か最初は見えてないので頭を使う。列挙したらソートと思っていたが、よく考えたらN以下のものが何個あるか単に数えればいい。解いてる途中で「1回以上現れる」という条件を忘れたが、修正すれば通った。

D - 756

N!の約数のうち約数をちょうど75個持つ正整数の個数を求めろという問題。「約数」が2回出てくるので意味を把握しにくい。N!が都合のいい特徴のありそうな数じゃないので、とっかかりがない。とりあえず単に七五数かの判定を考える。75=3*5^2(最初5で割って15*5にまず分解してしまったが75が25の倍数に見えない文脈ってあるんだね)。素因数分解したときに、例えば素数が3個出てきてp0^2*p1^4*p2^4とかなってれば約数は75個になる。他にもあって、全部でたぶん 4,4,2 24,2 14,4 74 の4つ。これを手動でソースコードに入れさせる問題なのがちょっと嫌な感じだ。100!の扱いは、わからんけどとりあえずN!を素因数分解するんじゃないかと思って実装した。既に10分くらい過ぎてて更に5分くらいかかったけど。それでもわからない。うんうんうなる。まさか解けないなんてことは。このころのことはよく覚えていない。とにかく解法がわかった。N!に各素因数が何個あるかの配列aから、各hについてh以上のa[j]が何個あるかの配列bを作る。そしてb[74]とか書く。4,4,2のパターンが心配だが、他の3つのケースを書いていく。組合せの重複を除くのが大変だったが、なんとか全部行けそう。しかし微妙に合わない。ここからデバッグ地獄だった。合ってるはずなのにサンプルが合わず、バグは全く見つからない。永遠とも思える時間が流れ、24,2を24,4で書いてることに気づいた。14,4からコピペして14のほうだけ直せばいいと思い込んでいたのだ。見た目もきれいだから気づかない。さて、ここを直してもサンプルは合わない(今回は全体的にサンプルが親切でそれでも難しいといういいセットだったね)。9だけずれてるので、6と3を見つけて/2を消すという普通のデバッグができる。2つの素因数は区別されるから割る必要がなかった。理解の浅い最初のころに書いたコードだ。提出してAC。1時間近くかかったABCは相当久しぶりじゃないか。

デバッグ力というか、この特殊な計算をバグらせにくく書く技術がなかった。そのバグを取り除いたうえでも組合せの間違いがあったが、これは、状況がまあまあわかっていても言語化しにくいゆえのミス。とにかく、b[24]とか調子に乗って書いて、そこをバグがわかるように書く技術もないのに自覚せず、ただ長時間悩むだけになった、それがよくない。