さてHard。
本番中には解けなかったけど面白い問題。
http://community.topcoder.com/stat?c=problem_statement&pm=12515
問題
一列に並んだN個のセルのどこかに(均等な確率で)トークンが入っているので、それを探したい。
トークンを探すとき、2分探索の手順でセル絞りこんでいく。
プレイヤーが最適な戦略で2分探索の対象セルを選択するとき、数A,Bに対して、A回~B回の範囲でトークンの位置を特定できる確率を最大化し、その確率を答える。
解法
この問題を言い換えると、
「N要素からなる2分木を作って、深さA~Bに入る要素数を最大化させる」
となる。
A~Bの要素を最大化するには、Aの時点で十分な幅を持たせつつ、A以前の深さではできるだけ分岐する深さを深くすればよい。
N<=10^6と大きく、AやBも大きい。
ただ、2分木は深さが20もあれば2^20≒10^6個の要素が入るので、A~A+20の深さがあれば10^6個全要素入るので、大きすぎるBは必要ない。
また、A-20までの深さでは分岐を全くしなくても、Aの深さで幅を2^20にできる。
よってAの前後20だけ深さを考えればよい。
A以降はすべて2分岐してA~Bの数を最大化させ、A-20以下の深さは分岐をしなくてよい。
以下のコードでは、深さAの時点の幅Wを1~2^(A-1)に変化させたとき、深さA~Bの間に入る要素数を計算している。
深さA~Bの間にはW*(2^(B-A+1)-1)個の要素が入るので、N-(深さ(A-1)までの最小要素)とのうち小さい方が幅Wでの要素数となる。
この処理はO(N logN)なので、N<=10^6でも間に合う。
class WellTimedSearch { public: double getProbability(int N, int A, int B) { int ON=N; int i,j,k; if(A>20) { N -= (A-20); B -= (A-20); A -= (A-20); } B=min(B,A+23); ll ma=0; for(i=1;i<=1<<(A-1);i++) { ll tot2=0; k=A-1; j=i; while(j>1) { j=(j+1)/2; tot2+=j; k--; } tot2 += k; ll tn=i*((1<<(B-A+1))-1); //_P("%d %lld %lld\n",i,tn,N-tot2); ma=max(ma, min(N-tot2,tn)); } return ma/(double)ON; } };
まとめ
確率の問題が二分木構築になるのが面白い。
あと、Forumにある最短回答がすごすぎる…。