首页 > 代码库 > bzoj 3566: [SHOI2014]概率充电器 树形DP

bzoj 3566: [SHOI2014]概率充电器 树形DP

首先普及一个概率公式 P(A+B)=P(A)+P(B)-P(AB)

题意:一些充电元件和导线构成一棵树,充电元件是否能充电有2种情况,

1、它自己有qi%的概率充电

2、与它相邻的元件通过导线给它充电(导线有p%的概率导通)

求最终充了电的元件的期望

题解:首先可以将元件能否充电分成3种情况考虑,

1、它自己给自己充好了电

2、它的儿子方向给它传送了电

3、它的父亲方向给它传送了电。

对于1,题目已经给出可以直接赋值,

对于2,可以通过一次树的深度遍历求得。pson[now]=pson[now] + pson[to]*edge_p - pson[now]*pson[to]*edge_p

对于3,麻烦一点,因为2中我们已经求得当前点(now)的儿子们给当前点的贡献,现在要求当前点给它的某个儿子(to)的贡献,这里要计算的话,就必须要排除之前to->now的,(想想若2中计算了to->now的概率,现在又要计算now->to的概率,明显出现了循环)具体公式推导见代码


/*
设y = now点除了从to点来的电 的充电概率,dp[i]表示i点的充电概率,pson[i]表示在i的子树中,i的充电概率
pson[to]*p + y - pson[to]*p*y=dp[now]
(1-pson[to]*p)*y=dp[now]-pson[to]*p
y=(dp[now]-pson[to]*p)/(1-pson[to]*p);
dp[to]=pson[to] + y*p - pson[to]*y*p;
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<iostream>
#include<cmath>
using namespace std;
#define eps 1e-6
struct edge
{
    int to,next;
    double p;
}e[2000000];
int head[500005],en;
double pson[500005];
double dp[500005];
double ans=0;
void add(int a,int b,double c)
{
    e[en].to=b;
    e[en].p=c;
    e[en].next=head[a];
    head[a]=en++;
}
void dfs(int now,int fa)
{
    for(int i=head[now];~i;i=e[i].next)
    {
        int to=e[i].to;double p=e[i].p;
        if(to==fa) continue;
        dfs(to,now);
        pson[now]=pson[now]+pson[to]*p-pson[now]*pson[to]*p;
    }
}
bool cmp(double a,double b)
{
    return fabs(a-b)<eps;
}
void dfs2(int now,int fa)
{
    ans+=dp[now];
    for(int i=head[now];~i;i=e[i].next)
    {
        int to=e[i].to;double p=e[i].p;
        if(to==fa) continue;
        double tmp=(1-pson[to]*p);
        if(cmp(tmp,0)) dp[to]=1.0;
        else
        {
            double y=(dp[now]-pson[to]*p)/(1.0-pson[to]*p);
            dp[to]=pson[to] + y*p - pson[to]*y*p;
        }
        dfs2(to,now);
    }
}
int main()
{
    int n,a,b,c;
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    en=0;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c/100.0);
        add(b,a,c/100.0);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%lf",&pson[i]);
        pson[i]/=100.0;
    }
    dfs(1,-1);
    dp[1]=pson[1];
    dfs2(1,-1);
    printf("%.6f\n",ans);
    return 0;
}