HDU5425Rikka with Tree II(数学期望)

题目链接:传送门 


题意:

给定一个以1为根的树我们知道了所有节点的深度,然后我们从中选出不少于2个节点,那么一共有2^n-1-n种方案,设f为所选节点的深度的最大值,g为所选节点的深度的次大值,然后求表达式 f*g/(f+1)/(g+1)的期望。


分析:

首先肯定要预处理出所有节点的深度,然后再来考虑没两个节点对所有节点的贡献。我们将所有节点的深度按从大到小排序设第i个节点是当前所选的最大值,第j(j>i)个节点是当前所选的次大值,那么这时所有的方案数为

2^(n-j) 那么这两个节点的共线就是 2^(n-j)/(2^n - n -1) *dep[i]*dep[j]/(dep[i]+1)/(dep[j]+1)。继续分析,由于题目要求的精度的误差是1-e6。

2^(n-j)/(2^n - n -1) ==> 1/[2^j - (n+1)/2^(n-j)]当j比较大的时候这个式子的值就特别小,对最终所求的答案的影响不大,因此我们可以只考虑j在100以内的点对对答案的贡献。


代码如下:


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;

const int maxn = 1e5+10;

vector<int >vc[maxn];

int dep[maxn];

void init() {
    for(int i=0; i<maxn; i++)
        vc[i].clear();
}

void dfs(int u,int pre,int dept) {
    dept++;
    dep[u]=dept;
    for(int i=0; i<vc[u].size(); i++) {
        int to = vc[u][i];
        if(to!=pre)
            dfs(to,u,dept);
    }
}

int n;

struct cmp {
    bool operator()(const int & a,const int & b) {
        return a>b;
    }
};

double calc(int x){
    if(x>70) return 0;
    double ans = n+1;
    while(x--) ans/=2.0;
    return ans;
}

int main() {
    while(~scanf("%d",&n)) {
        init();
        for(int i=2; i<=n; i++) {
            int x;
            scanf("%d",&x);
            vc[x].push_back(i);
            vc[i].push_back(x);
        }
        dfs(1,0,0);
        sort(dep+1,dep+n+1,cmp());
        double ans = 0;
        double tmp = 2.0;
        for(int i=2;i<=min(100,n);i++){
            tmp=tmp*2;
            for(int j=1;j<i;j++){
                ans+=(double )dep[i]*dep[j]/(dep[i]+dep[j])/(tmp-calc(n-i));
            }
        }
        printf("%.6lf\n",ans);
    }
    return 0;
}



你可能感兴趣的:(HDU5425Rikka with Tree II(数学期望))