传送门
就是按照随机访问孩子结点的方式,求每个结点的dfs序的期望值
对于以(u)为当前根结点时,那么随机访问它的一个孩子结点(v)的概率是(frac{1}{x}),(x)表示(u)的直接孩子结点个数
根据全排列,(u)的孩子结点访问的情况有(x!)种,那么按照期望公式(sum_{i = 1}^n概率 * 值)
其答案就是(frac{1}{x!}sum_{i = 1}^{x!} 值),那么考虑(v)第一个访问和最后一个访问的情况,都是有((x-1)!)次,且第一次访问的权值是(dp[u] + 1),最后一次访问的权值是(dp[u] + siz[u] - siz[v] - 1 + 1)。因为这是个全排列,所以说其实第1次访问加最后一次访问的权值和等于第二次访问加倒数第二次访问的权值和。。。其权值是(dp[u] + 1 + dp[u] + siz[u] - siz[v])
总共有(frac{x!}{2})个匹配,那么答案就是(frac{1}{x!} * (2dp[u] + 1 + siz[u] - siz[v]) * frac{x!}{2})
化简得到(dp[v] = dp[u] + (siz[u] - siz[v] + 1) / 2.0)
然后dfs一下就可以了,初始化是(dp[1] = 1.0)
struct Edge{
int to, next;
}e[M << 1];
int head[N], tot;
void add(int u, int v){
e[++tot].to = v;
e[tot].next = head[u];
head[u] = tot;
}
int a[N], siz[N];
double dp[N];
int current_time = 0;
void dfs(int u, int fath){
siz[u] = 1;
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if(v == fath) continue;
dfs(v, u);
siz[u] += siz[v];
}
}
void dfs1(int u, int fath){
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if(v == fath) continue;
dp[v] = dp[u] + (siz[u] - siz[v] + 1) / 2.0;
dfs1(v, u);
}
}
void solve(int kase){
int n; scanf("%d", &n);
for(int i = 2; i <= n; i++) {
scanf("%d", &a[i]);
add(a[i], i); add(i, a[i]);
}
dfs(1, 0);
dp[1] = 1;
dfs1(1, 0);
for(int i = 1; i <= n; i++)
printf("%.1lf%c", dp[i], "
"[i == n]);
}