P4284 [SHOI2014] 概率充电器

1 篇文章 0 订阅

很好的概率+树形Dp啊,这是第二次写了,第一次写是2月份,然后……写炸了,因为不会写从父亲到儿子的Dfs,所以就拖了将近……5个月(滑稽),总算还是A掉了

 原谅我看了题解验证我的公式是不是对的 

好啦开始吧:

 

这不是很显然的吗,首先定义Dp方程 F 和 G 分别表示儿子可以为父亲充上电的概率,和父亲可以为儿子充上电的概率

 

但是怎么转移呢?Emmm看样子很难转移啊,因为计算儿子被充上电的概率还是很繁琐的,因为可能有些儿子没有电,有些儿子又有,然而边又有可能不导电,如果要一个一个枚举,岂不炸了?

 

可不可以补集转化呢?重新定义 F 和 G 分别表示儿子不可以为父亲充上电的概率,和父亲不可以为儿子充上电的概率

 

那么转移就很方便了,先看 F 如何转移吧:

 

0、自己有电就不用考虑儿子充不充的上了,所以一开始不被儿子充电的概率是自己没电的概率

1、首先某个儿子自己没有电,那么就一定不可能充上电

2、其次某个儿子有电了,但是连接它的那条边没有导电,那么也不会被该儿子充上电

 

1 和 2 互无交集,所以其概率可以相加,那么所有儿子无法为他充上电的概率就是

F[u]=(1-selfcharge[u])*\prod_{son}^{allsons}(F[son]+(1-F[son])*(1-charge(u,v)))

可以转移 F 了!

 

下面来看如和转移 G:

0、首先考虑根节点的 G,根节点没有爸爸,所以他爸爸不给他充上电的概率为1

(这下子有了 G 的初值了,所以可以考虑如何转移呢)

1、那么某一个节点不被充电的概率应该是他爸爸不给他充电并且儿子也不给他充电的概率的乘积0

tmp=G[u]*F[u]/(F[v]+(1-F[v])*(1-charge(u,v)))

2、一个儿子结点不被爸爸充上电的概率是什么?是不是要算上 爸爸p不被充上电的概率,还要算上 p被充上电的概率但是又不导电的概率,也就是:

G[v]=tmp+(1-tmp)*(1-charge(u,v))

所以可以转移 G 啦

 

最后每一个点的答案就是

contribution[i]=1-G[i]*F[i]

统计一下就好了!

 

代码:

#include <bits/stdc++.h>

const  int  N = 1000000 + 5 ;
 
double  f [ N ] , g [ N ] , px [ N ] , e [ N ] ;
double  p , ans , ex ;
int  head [ N ] , nxt [ N ] , to [ N ] , cn ;
int  n , x , y ; 

void  create ( int  u , int  v , double  p ) {
    cn ++ ;
    px [ cn ] = p ;
    to [ cn ] = v ;
    nxt [ cn ] = head [ u ] ;
    head [ u ] = cn ;
}

void  dfs_son ( int  u , int  fr ) {
    int  v ;
    f [ u ] = 1 - e [ u ] ;
    for ( int  i = head [ u ] ; i ; i = nxt [ i ] ) {
        v = to [ i ] ;
        if ( v == fr )  continue ;
        dfs_son ( v , u ) ;
        f [ u ] *= ( f [ v ] + ( 1 - f [ v ] ) * ( 1 - px [ i ] ) ) ;
    }
}

void  dfs_fa ( int  u , int  fr ) {
    int  v ;
    for ( int  i = head [ u ] ; i ; i = nxt [ i ] ) {
        v = to [ i ] ;
        if ( v == fr )  continue ;
        double  t = g [ u ] * f [ u ] / ( f [ v ] + ( 1 - f [ v ] ) * ( 1 - px [ i ] ) ) ;
        g [ v ] = t + ( 1 - t ) * ( 1 - px [ i ] ) ; 
        dfs_fa ( v , u ) ;
    }
}

int  main ( ) {
    scanf ( "%d" , & n ) ;
    for ( int  i = 1 ; i < n ; i ++ ) {
        scanf ( "%d%d%lf" , & x , & y , & p ) ;
        create ( x , y , p / 100.0 ) ;
        create ( y , x , p / 100.0 ) ;
    }
    for ( int  i = 1 ; i <= n ; i ++ ) { 
        scanf ( "%lf" , & ex ) ;
        e [ i ] = ex / 100.0 ;
    }
    dfs_son ( 1 , 1 ) ;
    g [ 1 ] = 1.0 ;
    dfs_fa ( 1 , 1 ) ;
    for ( int  i = 1 ; i <= n ; i ++ )
        ans += 1 - f [ i ] * g [ i ] ;
    printf ( "%.6lf" , ans ) ;
    return  0 ; 
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值