HDU5425Rikka with Tree II(數學期望)

bigbigship發表於2015-09-07

題目連結:傳送門 


題意:

給定一個以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;
}



相關文章