首先根据题意,要求计算节点的所有k子集(k > 1)的对应的期望值,该期望值定义为子集中元素最大值和次大值调和平均值的1/2。
子集元素的值为其在树中的深度加一。
显然子集共有2n-n-1个,将元素值按照降序排列(存数列s),枚举最大值和次大值出现的位置,假设其分别为i,j且(0<i<j<n+1)。
考虑这一二元组对应的子集共有2n-j个,于是该二元组对答案的贡献值为2n-j/(2n-n-1) * s[i] * s[j] / (s[i] + s[j])。
考虑到s[i] * s[j] / (s[i] + s[j]) ≤ sqrt(s[i] * s[j]) / 2 < max(s[i], s[j]) < 1e5
当j > 100时,2n-j/(2n-n-1) < 2-100 < 1e-30
显然已经满足结果保留6位有效数字的要求,因此当n较大时只需枚举j < 100的情况即可。
注意一些表示细节。
http://acm.hdu.edu.cn/showproblem.php?pid=5425
1 #include <cstdio> 2 #include <cstring> 3 #include <vector> 4 #include <algorithm> 5 6 using namespace std; 7 8 const int maxn = 1e5 + 10; 9 10 vector<int> G[maxn]; 11 int d[maxn]; 12 int n; 13 double ans; 14 15 void dfs(int u, int dep){ 16 int d1 = G[u].size(); 17 for(int i = 0; i < d1; i++){ 18 int v = G[u][i]; 19 d[v] = dep + 1; 20 dfs(v, dep + 1); 21 } 22 } 23 24 bool cmp(int a, int b) { return a > b; } 25 26 double f(int num){ 27 if(num > 50) return 0; 28 double ans = n + 1; 29 while(num--) ans /= 2; 30 return ans; 31 } 32 33 int main(){ 34 while(~scanf("%d", &n)){ 35 for(int i = 1; i <= n; i++) G[i].clear(); 36 for(int i = 1, j; i < n; i++){ 37 scanf("%d", &j); 38 G[j].push_back(i + 1); 39 } 40 d[1] = 1; 41 dfs(1, 1); 42 sort(d + 1, d + n + 1, cmp); 43 ans = 0; 44 int rear = n > 100 ? 100 : n; 45 double tem = 2; 46 for(int j = 2; j <= rear; j++){ 47 tem *= 2; 48 for(int i = 1; i < j; i++){ 49 ans += (double)d[i] * d[j] / (d[i] + d[j]) / (tem - f(n - j)); 50 } 51 } 52 printf("%.6f ", ans); 53 } 54 return 0; 55 }