【Luogu】P4284概率充电器(概率树形DP)

  题目链接

  这题好神啊……

  设f[i]为i没电的概率,初始化$f[i]=1-q[i]$

  之后x的电有三个来源:

  1.x自己有电

  2.x的儿子给它传来了电

  3.x的父亲给它传来了电

  对于2和3操作分别做一次树形DP,第一次是用儿子推出父亲,第二次是用父亲推出儿子。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<cstdlib>
#define maxn 500050
#define eps 1e-9
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

struct Edge{
    int next,to;double val;
}edge[maxn*2];
int head[maxn],num;
inline void add(int from,int to,double val){
    edge[++num]=(Edge){head[from],to,val};
    head[from]=num;
}

double f[maxn];
double q[maxn];

void dfs(int x,int fa){
    f[x]=1-q[x];
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa)    continue;
        dfs(to,x);
        f[x]*=1-(1-f[to])*edge[i].val;
    }
    return;
}

void dgs(int x,int fa){
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa)    continue;
        double ret=1-f[x]/(1-(1-f[to])*edge[i].val);
        if(ret>eps&&f[to]>eps)    f[to]*=1-ret*edge[i].val;
        dgs(to,x);
    }
    return;
}

int main(){
    int n=read();
    for(int i=1;i<n;++i){
        int from=read(),to=read(),p=read();
        add(from,to,p*1.0/100);
        add(to,from,p*1.0/100);
    }
    for(int i=1;i<=n;++i)    q[i]=read()*1.0/100;
    dfs(1,1);
    dgs(1,1);
    double ans=0;
    for(int i=1;i<=n;++i)    ans+=1-f[i];
    ans+=eps;
    printf("%.6lf",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/cellular-automaton/p/8988868.html