FJUTOJ 校赛J 第十集 原来这一切都是真的 (树形概率dp)

题意:

有n个点组成的树,叶子节点是门,门有守卫的概率是这个点的编号/n,如果一个点连接的门的守卫个数或者路口的守卫个数时奇数那么这个点要设置一个守卫,问0也就是树根没有守卫的概率。

题解:

dp[u]表示树上节点u有守卫的概率,对于某个点u,以及儿子v,dp[u]=(1-dp[u])*dp[v]+dp[u]*(1-dp[v]),此时dp[u]可以表示在到v这个儿子为止守卫是奇数的概率(也就是这个点有守卫的概率),那么1-dp[u]就是表示守卫是偶数的概率,于是就可以和下一个儿子继续利用此公式dp[u]=(1-dp[u])*dp[v]+dp[u]*(1-dp[v]).

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define B(x) (1<<(x))
using namespace std;
typedef long long ll;
void cmax(int& a,int b){ if(b>a)a=b; }
void cmin(int& a,int b){ if(b<a)a=b; }
void cmax(ll& a,ll b){ if(b>a)a=b; }
void cmin(ll& a,ll b){ if(b<a)a=b; }
void add(int& a,int b,int mod){ a=(a+b)%mod; }
void add(ll& a,ll b,ll mod){ a=(a+b)%mod; }
const int oo=0x3f3f3f3f;
const ll MOD=100000007;
double dp[100005];
struct EDGE{
    int u,v,next;
}E[200005];
int head[100005],tol;
int n;
 
void Init(){
    memset(head,-1,sizeof head);
    tol=0;
}
 
void add_edge(int u,int v){
    E[tol].v=v;
    E[tol].next=head[u];
    head[u]=tol++;
}
 
void tree_dp(int u,int pre){
    dp[u]=0.0;
    int f=0;
    for(int i=head[u];i!=-1;i=E[i].next){
        int v=E[i].v;
        if(v==pre)continue;
        f=1;
        tree_dp(v,u);
        dp[u]=dp[u]*(1.0-dp[v])+(1.0-dp[u])*dp[v];
    }
    if(!f) dp[u]=1.0-1.0*u/n;
}
 
int main(){
    int T,u,v;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        Init();
        for(int i=1;i<n;i++){
            scanf("%d %d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        tree_dp(0,-1);
        printf("%.6lf\n",1.0-dp[0]);
    }
    return 0;
}


你可能感兴趣的:(FJUTOJ 校赛J 第十集 原来这一切都是真的 (树形概率dp))