[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;
}
原文地址:https://www.cnblogs.com/evenbao/p/14440862.html