本番全くわからなくて焦った…
解説読んでもあんまりピンと来なかったのですが、半日考え直してようやく理解できたので記事にします。
解法
無限に誰も排除されないことがあるけどどうしよう...となるので、とりあえずサンプル1がなんでこれでいいのかを解き明かさないといけない。
まず1周目で1人目が排除される確率は、2人目が排除される確率が、2周目に回って、1人目排除が...と続き、それぞれの総和がそれぞれの排除される確率であることがわかる。
もうちょっと考えると、1人目が排除される確率はとなって、2人目が排除される確率はとなることもわかる。
ここまでくると、として、みたいなものを計算したくなってくる。
であってなので、雑に考えるとを無限に飛ばせばはになって消えるでしょう、みたいな気持ちになると、上の式はと等しいだろうと予想できる。
実際色々ググってみたりするとこれが正しいことはわかる。
これをもとにサンプル1の話に戻ると、1人目が排除される確率は、2人目が排除される確率はとなり、それぞれを1から引けば他方が最後まで残る確率がわかるので、出力が正しいということがわかる。
もう少し抽象化して考えて、人いる状態で、先頭から人目(、以降単にと呼ぶ)が排除される確率はいくらだろうということを考えてみる。
1周目で排除される確率は、より前が全員の確率で生き延び、がの確率で排除されるため、となる(が0から始まることに注意)。
1周を誰も排除されず周り切る確率は明らかになので、2周目以降が排除される確率は、となり、これらの総和がが排除される確率になる。
これを式にすると、
となり、整理すると、
となることがわかる。長いので、以降はこの式をと表すことにする。
(ここまではコンテスト中に分かっていた点で、以降はコンテスト後の考察)
これを使って、以下のようなDPを考える。
人残っている時点で人目であるような人が最後まで生き残る確率
まず、1人しか残っていない時点で0番目であるような人が生き残る確率はなので、とわかる。
これを昇順に計算することを考えるが、遷移の考え方がちょっとこんがらがる。
たとえば、人いる状態で人目だった人が、人いる状態で人目になるにはどういう遷移をするか考えると、これは人目が排除されたときなので、
と遷移できる。同じように人目、人目...と遷移することを考えると、遷移式は以下のようになる。
右辺のそれぞれの項のの2つ目の添え字が「が生き残った人の中で何人目になるか」、が「がそこに移動するためにしかるべき人が排除される確率」であると考えると理解しやすい。
ここまでで解となるが、これを高速化するには、累積和を用いることができる。
具体的には、の前後から、適切にをかけて累積和をとる。先頭項は前からの累積和を単に足すだけで良い。以降の項は後ろからの累積和を用いるが、の値は動き続けるので、少し工夫が必要。
を思い出すと、が増えるたびに分母の次数が1つずつ下がっていくことがわかる。したがって、からを導くのはをかければよく、はをかけて導けるから、累積和に適切な回数をかければよい。
累積和の部分は言葉ではイマイチ上手く伝えられないので、以下の実装例から。
Submission #48642405 - Toyota Programming Contest 2023#8(AtCoder Beginner Contest 333)
あとがき
こういうゴリゴリ計算するのを要求される問題は破滅度が高いので大嫌いなんですが、こういうのを倒せないと黄色にはなれないんだなあと改めて思いました。(小並感)