[SHOI 2014] 概率充电器

题目描述:

QAQ…

题目分析:

P ( A + B ) = P ( A ) + P ( B ) P ( A B )
首先可以将元件能否充电分成3种情况考虑

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

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

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

1 是输入的
2 可以通过一次dfs做
3 麻烦一点,因为2中我们已经求得当前点(now)的儿子们给当前点的贡献,现在要求当前点给它的某个儿子(to)的贡献,这里要计算的话,就必须要排除之前to->now的,(想想若2中计算了to->now的概率,现在又要计算now->to的概率,明显出现了循环)

设y = now点除了从to点来的电 的充电概率,dp[i]表示i点的充电概率,pson[i]表示在i的子树中,i的充电概率

p s o n [ t o ] p + y p s o n [ t o ] p y = d p [ n o w ]

( 1 p s o n [ t o ] p ) y = d p [ n o w ] p s o n [ t o ] p

y = ( d p [ n o w ] p s o n [ t o ] p ) / ( 1 p s o n [ t o ] p ) ;

d p [ t o ] = p s o n [ t o ] + y p p s o n [ t o ] y p ;

别忘了卡精度…

题目链接:

Luogu 4284
BZOJ 3566

Ac 代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
const int maxm=510000;
const double eps=1e-8;
int head[maxm],net[maxm<<1],to[maxm<<1],cnt;
double p[maxm],dp[maxm],cost[maxm<<1],ans;
int n;
inline void addedge(int u,int v,double c)
{
    cnt++;
    to[cnt]=v,cost[cnt]=c,net[cnt]=head[u],head[u]=cnt;
}
inline bool check(double a,double b)
{
    return std::fabs(a-b)<eps;
}
void dfs1(int now,int fa)
{
    for(int i=head[now];i;i=net[i])
    if(to[i]!=fa)
    {
        dfs1(to[i],now);
        p[now]=p[now]+p[to[i]]*cost[i]-p[now]*p[to[i]]*cost[i];
    }
}
void dfs2(int now,int fa)
{
    ans+=dp[now];
    for(int i=head[now];i;i=net[i])
    if(to[i]!=fa)
    {
        double P=cost[i];
        double tmp=(1.0-p[to[i]]*P);
        if(check(tmp,0)) dp[to[i]]=1.0;  
        else  
        {  
            double y=(dp[now]-p[to[i]]*P)/tmp;  
            dp[to[i]]=p[to[i]]+y*P-p[to[i]]*y*P;  
        }  
        dfs2(to[i],now); 
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int u,v;
        double c;
        scanf("%d%d%lf",&u,&v,&c);
        addedge(u,v,c/100),addedge(v,u,c/100);
    } 
    for(int i=1;i<=n;i++)
     scanf("%lf",&p[i]),p[i]/=100;
    dfs1(1,0);
    dp[1]=p[1];
    dfs2(1,0);
    printf("%.6lf\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35914587/article/details/80216523