zoukankan      html  css  js  c++  java
  • [CF1060F] Shrinking Tree

    [题目链接]

    http://codeforces.com/contest/1060/problem/F

    [题解]

    首先显然可以计算以每个点为根的答案。

    直接求解概率比较困难 , 但由于总排列方案数是 ((N - 1)!) , 因此不妨令 (dp_{u , i}) 表示以 (u) 为根的子树 , 保留最后 (i) 条边 , 根的标号不变的概率之和。

    换言之 , (DP) 存的是 (sum_{p is sequence}{P(p)})

    (size_{u}) 表示以 (u) 为根的子树大小。

    考虑合并两棵子树 , 一棵树 (u)(x) 条保留边 , 另一棵有 (y) 条保留边。

    那么贡献即为 (f_{u , x} * g_{y} * {x + y choose x} * {s_{u} + s_{v} - x - y choose s_{u} - x})。 其中 , (g_{y}) 表示子树 (v) 保留了多少边。

    考虑如何计算 (g_{i}) , 不妨考虑 (f_{v , j}) 对其贡献。

    (j > i) 时 , 显然无贡献。

    (j = i) 时 , 说明 ((u , v)) 这条边必然在最后 (j) 条边之前合并完了 , 并且无需关心合并的标号问题 , 贡献为 ((size_{v} - j) * f_{v , j})

    (j < i) 时 , 说明 ((u , v)) 这条边必然在最后第 (j) 条边之前合并 , 且合并后标号为 (u)。 其贡献为 (0.5 * f_{v , j})

    这样我们就解决了合并问题 , 那么做一个近似于树上背包的 (DP) 即可。

    时间复杂度 : (O(N ^ 3))

    [代码]

    #include<bits/stdc++.h>
     
    using namespace std;
     
    typedef long long LL;
     
    #define rep(i , l , r) for (int i = (l); i < (r); ++i)
     
    const int MN = 1005;
     
    struct Edge {
    	int to , nxt;
    } E[MN << 1];
     
    int N , tot , head[MN] , siz[MN];
    double fac[MN] , tmp[MN] , f[MN][MN] , g[MN];
     
    inline void init() {
    	fac[0] = 1;
    	for (int i = 1; i <= N; ++i)
    		fac[i] = fac[i - 1] * i;
    }
    inline void AddEdge(int u , int v) {
    	E[++tot] = (Edge) {v , head[u]};
    	head[u] = tot;
    }
    inline double binom(int n , int m) {
    	return fac[n] / fac[m] / fac[n - m];
    }
    inline void DFS(int u , int fa) {
    	siz[u] = 1; f[u][0] = 1;
    	for (int i = 1; i <= N; ++i) f[u][i] = 0;
    	for (int i = head[u]; i; i = E[i].nxt) {
    		int v = E[i].to; if (v == fa) continue; DFS(v , u);
    		for (int k = 0; k <= N; ++k) tmp[k] = g[k] = 0;
    		for (int j = 0; j <= siz[v]; ++j)
    		for (int k = 0; k <= j; ++k)
    			if (k < j) g[j] += 0.5 * f[v][k];
    			else g[j] += (siz[v] - k) * f[v][k];
    		for (int j = 0; j < siz[u]; ++j)
    		for (int k = 0; k <= siz[v]; ++k)
    			tmp[j + k] += f[u][j] * g[k] * binom(j + k , j) * binom(siz[u] - 1 - j + siz[v] - k , siz[v] - k);
    		siz[u] += siz[v];
    		for (int k = 0; k < siz[u]; ++k) 
    			f[u][k] = tmp[k];  
    	}
    }
    int main() {
    	 
    	 scanf("%d" , &N);
    	 for (int i = 1; i < N; ++i) {
    	 	 int u , v; scanf("%d%d" , &u , &v);
    	 	 AddEdge(u , v) , AddEdge(v , u);
    	 }
    	 init();
    	 for (int i = 1; i <= N; ++i) {
    	 	 DFS(i , 0);
    	 	 printf("%.10lf
    " , f[i][N - 1] / fac[N - 1]);
    	 }
         return 0;
    }
    
  • 相关阅读:
    XML to bean
    监控Informix-Url
    ld-linux.so.2: bad ELF interpreter
    Oracle
    我的北漂生活:我来北京一个月了
    C/C++ 常量的定义与应用(编程中的常量)
    C/C++ 常量的定义与应用(编程中的常量)
    英文名字的昵称(亲切的叫法)
    英文名字的昵称(亲切的叫法)
    动态规划的关键 —— 子问题 & 公式化
  • 原文地址:https://www.cnblogs.com/evenbao/p/14440862.html
Copyright © 2011-2022 走看看