CF1060F Shrinking Tree

考虑枚举每个点来计算答案,枚举到一个点时,将该点看作是树的根。设 \(f_{x,i}\) 为在 \(x\) 的子树内进行删边,只考虑后 \(i\) 条边的编号分配,其余边任意分配,且根节点编号最后仍为 \(x\) 的概率之和。得 \(x\) 的最终答案为 \(\frac{f_{x,n-1}}{(n-1)!}\)

考虑合并子树,将 \(x\) 的儿子 \(y\) 合并到当前子树中,发现需要给 \(y\) 加上一条到 \(x\) 的边,用新子树的 \(DP\) 值来转移。设新的 \(DP\) 值为 \(g_i\),其定义和 \(f_{x,i}\) 相同。

考虑如何计算 \(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\)

合并子树时就是将 \(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}\)

#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;
}
posted @ 2021-01-26 20:18  lhm_liu  阅读(105)  评论(0编辑  收藏  举报