CF1060F Shrinking Tree

考慮枚舉每一個點來計算答案,枚舉到一個點時,將該點看做是樹的根。設 \(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;
}
相關文章
相關標籤/搜索