Description
Bob和Alice出去度蜜月,但Alice不慎走失,Bob在伤心过后,决定前去寻找Alice。
他们度蜜月的地方是一棵树,共有N个节点,Bob会使用下列DFS算法对该树进行遍历。
starting_time是一个容量为n的数组
current_time = 0
dfs(v):
current_time = current_time + 1
starting_time[v] = current_time
将children[v]的顺序随机排列 (每个排列的概率相同)
// children[v]v的直接儿子组成的数组
for u in children[v]:
dfs(u)
1是这棵树的根,Bob会从1出发,即运行dfs(1),现在他想知道每个点starting_time的期望值。
Solution
这场比赛异常的惨烈,前两题都是我不熟悉的概率期望题。
这道题的做题思路:直接dfs期望加优化——>不可能达到O(n)或O(n log n)——>找规律,不机智,太难找——推公式……
然后就推出来了。
不过我推的情况十分的复杂,我发现有些童鞋的推法十分的机智。
选择每个点出现在第i位前的概率为
i−1m
所以每个点出现在第i位前的期望贡献为
i−1m−1∗size[j]
那么每个点出现在第i位前的期望总和为
i−1m−1∑size[j]
用
sum
表示
∑size[j]
,不包括当前要算的节点本身。
那么所有节点在1到i为的排列中,本点的概率为
1m
所以就是
∑1m∗i−1m−1∗sum
最后算出来就是
1m∗∗sum∑i−1m−1
=1m∗∗sum∗(m−1)∗m2∗(m−1)
=sum2
那么到本点的答案就是
sum2+ans[fa[i]]+1
(sum不包括i)
O(n)搞定。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
typedef double db;
using namespace std;
const int maxn=100007;
int i,j,k,l,t,n,m;
int f[maxn],first[maxn*2],next[maxn*2],last[maxn*2],num;
db an[maxn],jie[maxn],ans[maxn],size[maxn],son[maxn];
void add(int x,int y){
last[++num]=y;next[num]=first[x],first[x]=num;
}
void dfs(int x,int y){
int i,o=0;;
an[x]=1;size[x]=1;
rep(i,x){
if(last[i]!=y){
dfs(last[i],x);o++;
size[x]+=size[last[i]];
}
}
son[x]=o;
}
void dfs1(int x,int y){
int i,o=0;
if(x>1)ans[x]=ans[y]+1+(size[y]-size[x]-1)/db(2);
rep(i,x){
if(last[i]!=y){
dfs1(last[i],x);o++;
}
}
}
int main(){
scanf("%d",&n);
jie[0]=1;
fo(i,1,n)jie[i]=jie[i-1]*i;
fo(i,2,n){
scanf("%d",&f[i]);
add(f[i],i);
}
ans[1]=1;
dfs(1,0);
dfs1(1,0);
fo(i,1,n)printf("%.1f ",ans[i]);
}