题意
给定一棵节点数为\(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\),由于我们求的是顺序,所以对于删去和未删去的边的两个集合,我们在内部会分别任意定序,最终转移方程为
最终答案为\(\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;
}