题目
一棵树,从根节点开始dfs,每层以随机顺序进入每个子节点,问走到每个点的时候期望经过了多少个点。
(这里经过多少个点指的是经过多少个不同的点,即经过一个点多次算一个)
(其实这个题不如说求期望dfn序)。
(nle 10^5)。
分析
一个很明显的思路就是:(f[x]=1+f[fa]+绕来绕去期望经过的点个数) 。从上往下dfs,问题就转化成了如何求每一个点(x)进入其子节点(v)之前期望经过的点个数。设绕来
绕去期望经过点个数为(g[x]) ,它的父亲有(n)个子节点。
计算这个东西有两种思路:
思路1
注意到这是一个dfs,所以我们如果进了一颗子树,那么它会走完整个子树再出来,而这个子树之前是没有走过的,即点数增加了( ext{size}[v]) 。((v)为与(x)同父亲的点)
这样我们就可以通过枚举之前走进了多少个子树来求:
[egin{aligned}
g[x]&=sum _{i=1}^{n-1} 任意不含x的i个的size的和*frac{1}{n}*frac{1}{n-1}*cdots*frac{1}{n-i} \
&=sum _{i=1}^{n-1} frac{(n-i-1)!}{n!}sum _{v
e x}i*size[v]* A_{n-2}^{i-1} \
&=sum _{i=1}^{n-1} frac{(n-i-1)!}{n!}sum _{v
e x}i*size[v]* frac{(n-2)!}{(n-i-1)!} \
&=sum _{v
e x}size[v] sum _{i=1}^{n-1}frac{(n-2)!}{n!} \
&=frac{1}{2}sum _{v
e x}size[v]
end{aligned}
]
思路2
把从(x)父亲进入这一层的顺序列出来,所有情况是它的全排列。(x)前面有(i)个数的概率为(frac{1}{n}), 前面(i)个数的和的期望为(frac{isum _{v e x}size[v]}{n-1}),所以所有情况为
[egin{aligned}
g[x]&=frac{1}{n}*frac{sum _{i=1}^{n-1}isum _{v
e x}size[v]}{n-1} \
&=frac{1}{2}sum _{v
e x}size[v]
end{aligned}
]
代码
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=1e5+1;
vector<int> g[maxn];
double f[maxn];
int size[maxn];
inline void add(int x,int y) {g[x].push_back(y);}
int Size(int x) {
int &sz=size[x]=1;
for (int v:g[x]) sz+=Size(v);
return sz;
}
void dfs(int x) {
for (int v:g[x]) f[v]=1.0+f[x]+(double)(size[x]-size[v]-1)/2.0,dfs(v);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
int n=read();
for (int i=2;i<=n;++i) add(read(),i);
f[1]=1;
Size(1);
dfs(1);
for (int i=1;i<=n;++i) printf("%.2lf%c",f[i],"
"[i==n]);
return 0;
}