ABC158

Eの単純なバグに気づくのに無限時間かかって、Fをやる時間が残らなかった。なんで俺はコンテスト中の貴重な時間をFを楽しむことではなくEの虚無デバッグに使ってるんだ。有声溜息(はあって息を出すのではなくはああって声を出す)を何回も出した。

A - Station and Bus

全部同じ文字かっていうのをずいぶん回りくどい言い方するから本当にその解釈でいいか確かめるのに時間食った。

(追記)いい実装ないかなーと思いながら実装してたけど、ソートして先頭と末尾を比較、std::setに入れてsize()といった方法があった。まあコード量は減らないけど。

B - Count Balls

なんじゃこのクソムズBは。ただ、内容は面白い。C=A+Bとして、周期はC。列の先頭から長さCのブロック(青はA個)が何個取れるかと、それを取り除いた残り(最初のA個だけ青)が何個かを求めればいい。

C - Tax Increase

税抜き10000円まで全探索。i円の商品に課される消費税は、i * 8 / 100のようにして整数演算だけで求められる。問題の性質といい難易度といい、完全にBとC逆だよね。

D - String Formation

第一印象は「dequeか!?」だった。よく読むと端に追加していくだけだから簡単だ。ただし、俺が簡単に解けるとは言っていない。まず、文字列が反転しているかどうかを表す変数を用意する。先頭と末尾に追加する文字たちを入れるためにstringを2つ用意する。文字を追加するときは、文字列が反転状態だったら逆(先頭だったら末尾)のほうへ追加する。あとは、先頭を反転させて、反転状態なら元の文字列も反転させて、結合して出力するだけ、と思いきやサンプルが合わない。ちょっとこんがらがった。先頭というのは元の文字列での先頭のことだ。つまり「反転状態なら反転させる」のは結合してからだ。提出してAC。たまたま合ってたけど、ちょっと甘え提出だった。

E - Divisible Substring

難しそう。下k桁や上k桁をPで割ったあまりは求まる。でもその情報からは、知りたい真ん中の区間はわからんよね。まあO(N^2)かけることはできないから関係ないか。が、全体をPで割ったあまりを求めておいて、そこから上と下を引いて10^kで割れば((10^k)^-1 mod Pを掛ければ)求まりそうじゃん。最下位がk桁目になるよう固定すれば何個あるかわかるのでは?(最上位の場所を固定すると10^kの部分が変わるので避けた)

ここからかなり時間は使ったが、なんとか解けそうな感じになった。コンテストの30分前にエネルギーとして牛乳を飲むという何も考えてない愚かな行動をとったのでトイレに行って、トイレの中で考えがおおむねまとまった。実装にかかるが、このときはまだ、123000と123を11で割ったあまりは異なるかもしれないけど11で割り切れるかどうかは同じであることに気づいてない。実装していって、P=5でも大丈夫か確認しながら実装していたが、ダメそうだとわかって、そのときに「Pが10を割り切るなら(下一桁で割り切れるか判定できるから)簡単に解けて、そうでないなら(10^k倍してもPで割り切れるかどうかは変化しないので思ったより楽に)今の方針で行ける」と気づいた。

ポイントは、上からi桁目が最下位になる個数を求めるときに、上位k桁(0<=k<i)をPで割ったあまりがt(0<=t<P)になることが何回あったかカウントしながらやっていくこと。長さPの配列をN個持つことはできないが、長さPの配列をN回変化させながら使えばいい。実装するが、明らかにおかしい値が出る。if (b < P) b += P;と書いていた(正しくはif (b < 0) b += P;)(bは全体からi桁目より下位を引いてあとは上位のあまりがいくつであればいいかという値)。これを直しても微妙にサンプルと合わない。コードを最初から読み直しても絶対に正しい(という感覚なのでつらい)。結局はbの符号が反対だった。これね、bの符号反転は最初に試したんだよ。でもそのときはb < Pって書くバグをまだ見つけてなかったんだ。b < Pとか書いた(これは初めて)せいで30分くらいロスした。Eが遅くなったことよりFをやる時間がなくなったことが悔しくてたまらない。

(追記)解説読んだらコード量めっちゃ減った。問題の性質が最初は全然わかってないから複雑な作りになってしまうが、力不足で仕方ないところだとは思う。

F - Removing Robots

なんでソート済みじゃないんだよ。ソートする。まず、各ロボットの移動がどこまで影響を及ぼすか。普通にやるとO(N^2)かかりそうだが、共通してるものがあるのでなんとかなりそうだ。後ろから考えるとよさそう。二分探索でどのロボットのところまで移動するか求め、その最後のロボットがどこまで影響を及ぼすかは既知なので、っと思ったけど違うわ接触した全てのロボットの影響の一番大きいやつだ。これはセグ木とかでできる。これで影響範囲がわかった。次にあるロボットの影響範囲を考えると、その区間にある全てのロボットの影響範囲は元のロボットの影響範囲に収まる。これを使うと、みたいなところで時間ですね。実装する時間がないとわかっていて、面白そうな問題を考える。もちろんいい気分ではなかった。

(追記)左からi番目のロボットから一番右までで何通りあるか考える。i番目のロボットを起動させるかどうかの2通り。起動させるとき右側の影響が及ばない範囲で何通りかわかればいい。起動させないとき自分より右全部の範囲で何通りかわかればいい。つまり後ろからDPできる。ほぼコンテスト中に考えていた方針でACできた。けっこう珍しいケースだね。時間が全然足りなかったけど時間さえあれば解けたというのは。