bzoj3566: [SHOI2014]概率充电器

这题点数很大,难以高斯消元

可是仔细想想一定要用吗?对于根节点他的概率可以通过子节点算出来

补集转化,设f[i]表示当前点没电的概率

对于孩子对父亲的贡献:f[x]=(1-q[x])*∏ 1-(1-f[y])*a[k].d

但是现在除了根以外其他的都不是真正的值,再dfs一次把父亲的贡献也算上,注意去掉自己的贡献

f[y]*=(1-(1-f[x]/((1-f[y])*a[k].d))*a[k].d)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

struct node
{
    int x,y,next;double d;
}a[1100000];int len,last[510000];
void ins(int x,int y,double d)
{
    len++;
    a[len].x=x;a[len].y=y;a[len].d=d;
    a[len].next=last[x];last[x]=len;
}
double q[510000],f[510000];
void dfs1(int x,int fr)
{
    f[x]=1-q[x];
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=fr)
        {
            dfs1(y,x);
            f[x]*=1-(1-f[y])*a[k].d;
        }
    }
}
void dfs2(int x,int fr)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=fr)
        {
            if((1-(1-f[y])*a[k].d)>1e-8)
            {
                double r=1-f[x]/(1-(1-f[y])*a[k].d);
                f[y]*=(1-r*a[k].d);
            }
            dfs2(y,x);
        }
    }
}

int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n,x,y;double dd;
    scanf("%d",&n);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%lf",&x,&y,&dd);dd/=100;
        ins(x,y,dd),ins(y,x,dd);
    }
    for(int i=1;i<=n;i++)
        scanf("%lf",&q[i]), q[i]/=100;
    dfs1(1,0);
    dfs2(1,0);
    double ans=0;
    for(int i=1;i<=n;i++)ans+=1-f[i];
    printf("%.6lf\n",ans);
    return 0;
}

 

posted @ 2018-11-06 11:22  AKCqhzdy  阅读(130)  评论(0编辑  收藏  举报