codeforces 696B puzzle
求随机dfs序的期望。
我们来看到一个点之后是什么情况。
一个点p有很多儿子,其中一个是x;
如果要求x的期望,那么先看:
如果从父亲直接到这个点,那就是父亲的期望加一;
如果还走其他的点,那么最后一步肯定还是要加一(因为你总要花一步从别的店跳过来),所以无论怎样都要父亲的期望加一。剩下来再看,如果跳到别的点,那么就是说明有附加的期望。下面再加上这一部分就行了。
那么现在就是求多出来的这一部分期望。
怎么求呢,首先看清一个问题。
设p0=(son_num[p]-son_num[x])/(all_son-1);
这个是我跳到一个子树的期望(平均子树的期望),由于是期望,所以取了平均。
期望=概率*权值。
下面开始计算:
跳一个其余的子树增加的期望是
(n-1)/n*1/(n-1)*p0;//选一个其余儿子,再选一个x的概率乘一次p0。
跳两个是
(n-1)/n*(n-2)/(n-1)*1/(n-2)*2p0;//选两个其余儿子乘两次p0。
。。。(一共有n-1项)
加起来就是(1+...+n-1)*(son_num[p]-son_num[x])/(n-1)/n;
也就是(son_num[p]-son_num[x])/2.0;
又因为存的时候会带上根节点,所以要再减一;
也就是(son_num[p]-son_num[x]-1)/2.0;
完美~然后上代码。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <string> #include <cstdlib> #include <vector> #include <set> #include <map> using namespace std; int a[100100]; vector<int>edge[100100]; int num_son[100100]; double ans[100100]; int dfs_fir(int root) { num_son[root]=1; for (int i=0;i<edge[root].size();i++) { num_son[root]+=dfs_fir(edge[root][i]); } return num_son[root]; } int dfs_sec(int root) { for (int i=0;i<edge[root].size();i++) { int son=edge[root][i]; ans[son]=1.0*(num_son[root]-num_son[son]-1)/2.0+ans[root]+1.0; dfs_sec(son); } } int main() { int n; scanf ("%d",&n); for (int i=2;i<=n;i++) { scanf ("%d",&a[i]); edge[a[i]].push_back(i); } dfs_fir(1); ans[1]=1.0; dfs_sec(1); for (int i=1;i<n;i++) printf ("%.1f ",ans[i]); printf ("%.1f\n",ans[n]); return 0; }