先日こんな問題が出ました ABC364-D K-th Nearest
(概要)
数直線上に
個の点があるので、以下のクエリに
個答えてください。
個の点のうち座標
から
番目に近い点との距離を求めよ
も
も
ありうるので、愚直に調べるのはTLEします。
じつは
番目に近い点の距離を求めよ
という問題はにぶたん (二分探索) が相性が良いです。
にぶたんで解ける問題にするために、問題の言い換えが必要なのですが、
これは筆者がとても苦手としていて毎回10分ぐらい悩むので、次に出てきたら使えるようにメモしておきます。
以下が結論です。
から
番目に近い点の距離を求めよ
↓(言い換え)
- 「
から距離
以下の点が何個ある?」の答えが
個以上になる最小の
はいくつ?
です。
どういうこと?ほんとにそうなの?は後述するとして、この言い換えができれば
「距離以下の点が何個ある?」の問いに高速に答えられれば、にぶたんが使える!となります。
実際、この問題では
の左右両側を調べる必要がある点に注意ですが、
と
が決まっていれば
bisect_right(A, x+d) - bisect_left(A, x-d)等のようにすれば「から距離
以下の点の個数」が求まります
これを使ってにぶたんをすると問題をACできます (にぶたん in にぶたん 1)
どういうこと?ほんとにそうなの?
左右を見る問題はちょっと複雑なので単純にします。
距離に限らず、比較可能な数値であれば適用できます。
ソートされた数列があり、これの「小さい方から番目」を求めることにします。2
]
これの、
「小さい方から番目」の値
と、
「以下の値は何個?」の答えが
個以上になる最小の
はいくつ?
の答えが一致することを確認します。
まず、以下の値は何個?を小さい方から調べてしまいます。
以下の値は?:
個
以下の値は?:
個
以下の値は?:
個
以下の値は?:
個
以下の値は?:
個
以下の値は?:
個
以下の値は?:
個
のようになっています。
例えば のときを見ていきます。
が小さい方から見ていって、
以下の値は?が初めて
個以上になるのは、確かに
の時で、
の
番目の値は
です。
また、のとき、
以下の値は?が初めて
個以上になるのは、
の時で、
の
番目の値は
です。
同様に、のとき、
以下の値は?が初めて
個以上になるのは、
の時で、
の
番目の値は
です。
他の場合を試しても、確かに言い換えが正しいことがわかります。3
(おまけ) 言い換えのバリエーション
- 「スコアが小さい方から
番目の人」を求めよ
↓ (言い換え)
①「スコア以下の人は何人?」の答えが
人以上になる最小の
はいくつ?
②「スコアより大の人は何人?」の答えが
人を超えない最大の
はいくつ?
③「スコア以上の人は何人?」の答えが
人以上になる最大の
はいくつ?
④「スコア未満の人は何人?」の答えが
人を超えない最小の
はいくつ?
- 「スコアが大きい方から
番目」「大きい方から
位」の場合は以下の通りです。
↓ (言い換え)
⑤「スコア以上の人は何人?」の答えが
人以上になる最大の
はいくつ?
⑥「スコア未満の人は何人?」の答えが
人を超えない最小の
はいくつ?
⑦「スコア以下の人は何人?」の答えが
人以上になる最小の
はいくつ?
⑧「スコアより大の人は何人?」の答えが
人を超えない最大の
はいくつ?
頑張ってバリエーションを考えてみたものの、とりあえず①と⑤があれば良さそうですかね。^^
おわり
にぶたんマスターになろう!