【BZOJ3566】【SHOI2014】概率充电器 树形DP 概率DP

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/46460415");
}

题解:

首先无根树转化为有根树。
fi: 表示 i 节点由其子树内节点充【不上】电的概率。
gi: 表示 i 节点由其父亲节点充【不上】电的概率。
hi: hi=fi+(1fi)(1线) 表示对父亲 fi 的贡献。

先dfs一遍,
fi=(1)×hson
原因:各子要么本身就没电,直接贡献上去、要么有电,那么就需要向上的导线断掉。

再dfs一遍,
t=hv<eps?0:gxfxhv;
gv=t+(1t)(1线(v,x));

ans=1figi

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 501000
using namespace std;
struct Eli
{
    int v,n;
    double l;
}e[N<<1];
int head[N],cnt;
inline void add(int u,int v,double l)
{
    e[++cnt].v=v;
    e[cnt].l=l;
    e[cnt].n=head[u];
    head[u]=cnt;
}
double f[N],g[N],h[N];
int fa[N];
void dfs1(int x,int p)
{
    fa[x]=p;
    int i,v;
    for(i=head[x];i;i=e[i].n)
    {
        v=e[i].v;
        if(v==p)continue;
        dfs1(v,x);
        h[v]=f[v]+(1-f[v])*(1-e[i].l);
        f[x]*=h[v];
    }
}
void dfs2(int x,int p)
{
    double t;
    int i,v;
    for(i=head[x];i;i=e[i].n)
    {
        v=e[i].v;
        if(v==p)continue;
        t=h[v]<1e-6?0:g[x]*f[x]/h[v];
        g[v]=t+(1-t)*(1-e[i].l);
        dfs2(v,x);
    }
}
int n;
int main()
{
    freopen("test.in","r",stdin);

    int i,j,k;
    int a,b,c;

    scanf("%d",&n);
    for(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(i=1;i<=n;i++)
    {
        scanf("%d",&c);
        f[i]=1-c/100.0;
    }
    dfs1(1,0);
    g[1]=1,dfs2(1,0);

    double ans=0.0;
    for(i=1;i<=n;i++)ans+=1-f[i]*g[i];
    printf("%.6lf\n",ans);

    return 0;
}

你可能感兴趣的:(树形DP,概率DP,BZOJ3566,SHOI2014,概率充电器)