• [CF1060F]Shrinking Tree[树dp+组合计数]


    题意

    你有一棵 (n) 个点的树,每次会随机选择树上的一条边,将两个端点 (u,v) 合并,新编号随机为 (u,v)。问最后保留的编号分别为 (1)(n) 的概率。

    (nleq 50)

    分析

    • 考虑枚举钦定一个编号为 (ans) 之后以他为根跑一次树dp。

    • 思考一下操作的执行过程。首先,操作连接 (rt) 和他的儿子 (v) 的边时,必须保留 (rt) 的编号,然后合并掉 (v) ,可以看成是 (rt) 将他的编号传递给了 (v) ((rt) 变成了一个大点)。那么此时对于 (v) 子树内的编号变化就是一个子问题了。

    • (f_{u,i}) 表示当 (rt) 下放到 (u) 时, (u) 的子树边还有 (i) 条没有合并,所有删边方案最后保留 (rt) 的概率和。
      (g_{u,i}) 表示当 (rt) 下放到 (u) 的父亲时, (u) 的子树边+返祖边还有 (i) 条没有合并,所有删边方案最后保留 (rt) 的概率和。

    • 考虑合并 (u) 的儿子 (v)(u ightarrow v) 这条边可以在 2 个不同的时间段合并。

    1.在编号下放到 (u) 之后。假设下放到 (u)(v) 子树内有 (x) 条边,那么在删除 (u ightarrow v) 之后 (v) 的子树内有 (leq x) 条边,所以 (g_{v,x}+=0.5*sum_{i=0}^{min({son}_v-1,x)}f_{v,i}) ,因为此时保留 (rt) 的概率是 (0.5)

    2.在编号下放到 (u) 之前。所以下放到 (u)(u)(v) 已经看成是一个点,等价于下放到 (v)(u ightarrow v) 可以在删除 (v) 子树内的 ({son}_v-1-x) 条边中的任何空隙删除,所以 (g_{v,x}+=f_{v,x}*({son}_v-x))

    • 考虑儿子 (x)(y) 之间的合并,发现他们之间的删边顺序互不影响,容易得到:

    [f_{u,i}=sum_{a=0}^{{son}_x}sum_{b=0}^{{son}_y}g_{x,a}*g_{y,b}*inom{a+b}{a}inom{{son}_x-a+{son}_y-b}{{son}_x-a} ]

    • 最后的答案除以 ((n-1)!) 即可。

    • 总时间复杂度为 (O(n^4))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].lst,v=e[i].to)
    #define rep(i,a,b) for(int i=a;i<=b;++i)
    #define pb push_back
    typedef long long LL;
    inline int gi(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch))	{if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
    	return x*f;
    }
    template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
    template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
    typedef double db;
    const int N=54;
    int n,edc;
    int head[N],son[N];
    db f[N][N],fac[N],g[N];
    struct edge{
    	int lst,to;
    	edge(){}edge(int lst,int to):lst(lst),to(to){}
    }e[N*2];
    void Add(int a,int b){
    	e[++edc]=edge(head[a],b),head[a]=edc;
    	e[++edc]=edge(head[b],a),head[b]=edc;
    }
    db C(int n,int m){
    	return fac[n]/fac[m]/fac[n-m];
    }
    void dfs(int u,int fa){
    	f[u][0]=1;son[u]=1;
    	go(u)if(v^fa){
    		dfs(v,u);int tot=son[u]-1+son[v];
    		fill(g,g+tot+1,0);
    		for(int a=son[u]-1;~a;--a)
    		for(int b=son[v];~b;--b)
    		g[a+b]+=f[u][a]*f[v][b]*C(a+b,a)*C(tot-a-b,son[v]-b);
    		son[u]+=son[v];
    		rep(a,0,son[u]-1) f[u][a]=g[a];
    	}
    	if(fa){
    		fill(g,g+son[u]+1,0);
    		for(int a=son[u]-1;~a;--a){
    			g[a]+=f[u][a]*(son[u]-a);
    			for(int b=a+1;b<=son[u];++b)
    			g[b]+=f[u][a]*0.5;
    		}
    		rep(a,0,son[u]) f[u][a]=g[a];
    	}
    }
    int main(){
    	n=gi();
    	rep(i,1,n-1) Add(gi(),gi());
    	fac[0]=1;
    	rep(i,1,n) fac[i]=fac[i-1]*i;
    	rep(i,1,n){
    		memset(f,0,sizeof f);
    		dfs(i,0);
    		printf("%.10lf
    ",f[i][n-1]/fac[n-1]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Mysql高级第一天(laojia)
    Mysql初级第三天(wangyun)
    Mysql初级第二天(wangyun)
    Mysql初级第一天(wangyun)
    Spring的源码解析
    JAVA8新特性
    java8
    JMM内存模型
    JAVA并发工具类
    mybatis
  • 原文地址:https://www.cnblogs.com/yqgAKIOI/p/10055359.html
Copyright © 2020-2023  润新知