考慮枚舉每一個點來計算答案,枚舉到一個點時,將該點看做是樹的根。設 \(f_{x,i}\) 爲在 \(x\) 的子樹內進行刪邊,只考慮後 \(i\) 條邊的編號分配,其他邊任意分配,且根節點編號最後仍爲 \(x\) 的機率之和。得 \(x\) 的最終答案爲 \(\frac{f_{x,n-1}}{(n-1)!}\)。c++
考慮合併子樹,將 \(x\) 的兒子 \(y\) 合併到當前子樹中,發現須要給 \(y\) 加上一條到 \(x\) 的邊,用新子樹的 \(DP\) 值來轉移。設新的 \(DP\) 值爲 \(g_i\),其定義和 \(f_{x,i}\) 相同。git
考慮如何計算 \(g_i\),枚舉 \((x,y)\) 這條邊在倒數第 \(j\) 步被刪掉。當 \(i \geqslant j\) 時,這裏要求刪掉 \((x,y)\) 時必須保留 \(x\),有 \(\frac{1}{2}\) 的機率,以前的邊的選擇是任意的,以後必須保留 \(x\),這裏刪去了 \((x,y)\),保留 \(y\) 和保留 \(x\) 等價,所以將 \(\frac{1}{2}f_{y,j-1}\) 貢獻到 \(g_i\)。當 \(i<j\) 時,\((x,y)\) 這條邊不用考慮編號分配,所以將 \(f_{y,i}\) 貢獻到 \(g_i\)。spa
合併子樹時就是將 \(f_{x,i}g_j\binom{i+j}{i}\binom{siz_x-1-i+siz_y-j}{siz_x-1-i}\) 貢獻到 \(f_{x,i+j}\)。code
#include<bits/stdc++.h> #define maxn 110 using namespace std; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n; int siz[maxn]; double fac[maxn],f[maxn][maxn],g[maxn]; struct edge { int to,nxt; edge(int a=0,int b=0) { to=a,nxt=b; } }e[maxn]; int head[maxn],edge_cnt; void add(int from,int to) { e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt; } double C(int n,int m) { return fac[n]/fac[m]/fac[n-m]; } void dfs(int x,int fa) { f[x][0]=siz[x]=1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa) continue; dfs(y,x); for(int j=0;j<=siz[y];++j) for(int k=1;k<=siz[y];++k) f[0][j]+=k<=j?f[y][k-1]/2:f[y][j]; for(int j=siz[x]-1;j>=0;--j) for(int k=siz[y];k>=0;--k) g[j+k]+=f[x][j]*f[0][k]*C(j+k,j)*C(siz[x]-1-j+siz[y]-k,siz[x]-1-j); siz[x]+=siz[y]; for(int i=0;i<siz[x];++i) f[x][i]=g[i]; memset(g,0,sizeof(g)),memset(f[0],0,sizeof(f[0])); } } int main() { read(n); for(int i=1;i<n;++i) { int x,y; read(x),read(y); add(x,y),add(y,x); } fac[0]=1; for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i; for(int i=1;i<=n;++i) { memset(f,0,sizeof(f)),dfs(i,0); printf("%.10lf\n",f[i][n-1]/fac[n-1]); } return 0; }