题意

给定一棵节点数为\(n\)的树,每次随机选取一条边,将该边的两端点合并成一个新点,新点的标号等概率从两端点中选取。新点与所有和两端点连边的点连边。

问每个点最后能剩下的概率

\(n\leq50\)

sol

观察到最后\(n-1\)条边都会删除,不妨将这些边定序,第\(i\)个点最后剩下的概率,为满足删到最后留下第\(i\)个点的定序方案数,除以总定序方案数\((n-1)!\)

现在考虑如何求最后留下第\(i\)个点的方案数,我们令\(f[i][j]\)表示以\(i\)为最后留下的点(为根),在\(i\)的子树内删去了\(j\)条边,将删去的边与未删去的边一同排序的定序方案数。

首先我们先讨论每个点有且仅有一个子树(即树为一棵链的情况)

假设我们已经知道了\(f[x=son[i]][0\text{---}size[x]]\)的值,现在要转移到\(f[i][0\text{---}size[i]]\)。我们暴力枚举\(j\in[0\text{---}size[i]]\),\(k\in[0\text{---}size[x]]\),对于\(j,k\)的大小关系分类讨论,进行转移。

\(1.j<k\)显然是不可能发生的,故直接略去。

\(2.j>k\),我们先考虑\(f[x][k]\)表示的概念,即在\(x\)子树中删了\(k\)条边,且根在\(x\)的方案数,而\(f[i][j]\)则必须要将\(x\)转移到\(i\),因为涉及到节点的转移,所以要乘个\(inv2\),转移方程为
\(f[i][j]+=f[x][k]\times inv2\)

\(3.j=k\)因为两种相等,所以显然根节点在已删掉的边中无法转移,那么\(x->i\)的转移就存在于后面未被删掉的边中,由于有顺序,所以该删除操作可以任意掐在所有未删的边之后的删除中,共有\(size[x]-j\)种方案

转移方程为
\(f[i][j]+=f[x][k]\times(size[x]-j)\)

对于边界,显然,一个不删也是一种方案,即\(f[i][0]=1\)

下面我们来讨论多子树的转移。

假设我们已经得到了前面若干棵子树的答案,设为\(f'[i_1][j_1]\),现在已知了新的一棵子树的\(f[i_2][j_2]\),要将这两部分答案合并。

假设原来的一部分的总边数为\(x\),新合成进来的一部分边数为\(y\),由于我们求的是顺序,所以对于删去和未删去的边的两个集合,我们在内部会分别任意定序,最终转移方程为

\[f'[i_1][j_1+j_2]+=f'[i_1][j_1]\times f[i_2][j_2]\times\tbinom{j_1+j_2}{j_1}\times \tbinom{x+y-j_1-j_2}{x-j_1} \]

最终答案为\(\frac{f[1][n-1]}{n!}\)

#include <bits/stdc++.h>
using namespace std;
#define N 114514
#define int long long
int nxt[N<<1],to[N<<1];
int head[N],num;
inline void add_edge(int u,int v){
	nxt[++num]=head[u];
	to[num]=v;
	head[u]=num;
}
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
	return x*f;
}
#define M 66
#define p 998244353
int inv2=499122177;
int siz[M],f[M][M]/*这里为f'[i][j]*/,g[M];/*g[x]用来临时存f[x][k]*/
int ljw[M];//多子树转移时的工具数组
int C[M][M]; 
int n;
inline void file(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
}
inline void dfs(int x,int fa){
	siz[x]=f[x][0]=1;
	for(int i=head[x];i;i=nxt[i]){
		int v=to[i];
		if(v==fa)continue;
		dfs(v,x);
		for(int j=0;j<=siz[v];j++){
			g[j]=0;
			for(int k=0;k<siz[v];k++){
				if(k<j)g[j]+=inv2*f[v][k]%p;
				else if(k==j)g[j]+=f[v][j]*(siz[v]-j);
				
				if(g[j]>p)g[j]-=p;
			}
		}
		for(int j=0;j<siz[x]+siz[v];j++)ljw[j]=0;
		for(int j=0;j<siz[x];j++){
			for(int k=0;k<=siz[v];k++){
				ljw[j+k]=(ljw[j+k]+f[x][j]*g[k]%p*C[j+k][j]%p*C[siz[x]+siz[v]-j-k-1][siz[x]-j-1]%p)%p;
				
			}
		}
		siz[x]+=siz[v];
		for(int j=0;j<siz[x];j++)f[x][j]=ljw[j];
	}
	return;
}
inline int ksm(int a,int b){
	int ans=1ll;
	while(b){
		if(b&1)ans=(ans*a)%p;
		a=(a*a)%p;
		b>>=1;
	}
	return ans%p;
}
signed main(void){
	//file();
	n=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		add_edge(u,v);
		add_edge(v,u);
	}
	int facinv=1;
	for(int i=2;i<n;i++)facinv=facinv*i%p;
	facinv=ksm(facinv,p-2);
	C[0][0]=1;
	for(int i=1;i<=n;i++){
		C[i][0]=1;
		for(int j=1;j<=i;j++){
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
		}
	}
	for(int i=1;i<=n;i++){
		dfs(i,0);
		printf("%lld\n",f[i][n-1]*facinv%p);
	}
	return 0;
}