TopCoder Open 2014 Round1B Hard EagleInZoo
問題
根付き木が与えられる。
この木に対して次の操作をK回行う。
K番目の操作を行ったとき、K番目の鳥がどこかの頂点に入れる確率を求めよ。
操作:
根に鳥が来る。
鳥が今いる頂点に、他の鳥がまだいなければその頂点に入って終了。
そうでない場合、子を等確率で一つ選び、そこに移動して同様のことを行う。
子がなかった場合、どこにも入らずに終了。
制約条件
木の頂点≦51
K≦100
方針
メモ化再帰。
rec(c, num)を、頂点cを根とする部分木にnum羽の鳥が来たとき、
最後の一羽がどこかに止まれる確率とする。
num = 1だったら根に止まればよいので終了。
それ以外の場合は、最後の一羽がどこに移動するかで場合分け。
最後の一羽が行く頂点にx羽が行くとすると、
最後の一羽が止まれる確率は
rec(child, x)を含む式になって、再帰で計算できる。
最後の一羽以外の鳥が、childの頂点にx羽行く確率は、
全ての子の頂点が等確率で選ばれるので、確率pで表が出るコインをm枚投げて、
x回表が出る確率として求めればよい。
ソースコード
vector<vi> e; long double dp[51][110]; long double C[110][110]; long double rec(int c, int num){ long double &res = dp[c][num]; if(res >= 0) return res; if(num == 1) return res = 1; res = 0; rep(i, e[c].size()) rep(j, num - 1){ res += C[num - 2][j] * pow(1.0l / e[c].size(), j) * pow(1 - 1.0l / e[c].size(), num - 2 - j) / e[c].size() * rec(e[c][i], j + 1); } return res; } class EagleInZoo { public: double calc(vector <int> parent, int K) { int n = parent.size() + 1; e.clear(); e.resize(n); memset(dp, -1, sizeof(dp)); rep(i, n - 1) e[parent[i]].pb(i + 1); rep(i, 110) rep(j, i+1) C[i][j] = j==0||j==i ? 1 : C[i-1][j] + C[i-1][j-1]; return (double)rec(0, K); } };