【題解】Luogu P4284 [SHOI2014]機率充電器

原題傳送門

咱們知道,每一個電器充電對充電電器數的貢獻都是相等的1,因此若第\(i\)個電器有\(p_i\)的機率充電時

\[E=\sum_{i=1}^np_i\]

咱們考慮如何求\(p_i\),根據樹形dp的套路,確定是本身子樹的貢獻和非本身子樹貢獻的結合

\(f_i\)表示本身及本身的子樹不能給本身充電的機率,\(g_i\)表示非子樹節點和本身不能給本身充電的機率,易知

\[p_i=1-f_ig_i\]

咱們考慮如何求\(f_i\),\(g_i\)

對於\(f_i\):

\[f_i=(1-direct_i)*\prod_{v \in son_i}(f_v+(1-f_v)*(1-edge[i->v].p))\]

解釋一下:首先要本身不直接通電,而後任意一個兒子要麼不通電,要麼通電且與本身的電線不通

對於\(g_i\)

\[tmp=\frac{g_{fa}*f_{fa}}{f_i+(1-f_i)*(1-edge[fa->i].p)}\]

\[g_i=tmp+(1-tmp)*(1-edge[fa->i].p)\]

\(tmp\)表示除了本身和本身的子樹通電,本身父親通電的機率。那麼從非子樹和本身來的電就是父親不通電的機率與父親通電但本身與父親電線不通的機率之和

注意:題目給的是百分數,要處以\(100.0\);咱們擬定\(1\)爲根節點,因此初始條件爲\(g_1=1\)

#include <bits/stdc++.h>
#define db double 
#define N 500005
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
struct edge{
    int to,next;
    db w;
}e[N<<1];
int head[N],cnt=0;
inline void add(register int u,register int v,register db w)
{
    ++cnt;
    e[cnt].to=v,e[cnt].next=head[u],e[cnt].w=w;
    head[u]=cnt;
}
int n;
db a[N],g[N],f[N],ans;
inline void dfs1(register int x,register int fa)
{
    f[x]=1.0-a[x];
    for(register int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa)
            continue;
        dfs1(v,x);
        f[x]*=f[v]+(1.0-f[v])*(1.0-e[i].w);
    }
}
inline void dfs2(register int x,register int fa)
{
    for(register int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa)
            continue;
        double tmp=g[x]*f[x]/(f[v]+(1.0-f[v])*(1.0-e[i].w));
        g[v]=tmp+(1.0-tmp)*(1.0-e[i].w);
        dfs2(v,x);
    }
}
int main()
{
    n=read();
    for(register int i=1;i<n;++i)
    {
        int u=read(),v=read(),w=read();
        add(u,v,w/100.0),add(v,u,w/100.0);
    }
    for(register int i=1;i<=n;++i)
    {
        int x=read();
        a[i]=x/100.0;
    }
    dfs1(1,0);
    g[1]=1.0;
    dfs2(1,0);
    for(register int i=1;i<=n;++i)
        ans+=(1.0-g[i]*f[i]);
    printf("%.6lf",ans);
    return 0;
}
相關文章
相關標籤/搜索