BZOJ-3566: [SHOI2014]概率充电器(树形DP)

作者: AmadeusChan | 来源:发表于2019-02-16 20:08 被阅读0次

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3566

    树形DP,设up[ v ]为在v的子树中,v的充电概率,设dp[ v ]为在整颗树中v的充电概率,那么:

    令merge( x , y ) = x + y - x * y(容斥原理)

    up[ v ] = merge( up[ v ] , up[ child( v ) ] * e( v , child( v ) ) )

    然后,dp[ root ] = up[ root ],对于一条边(v , u),若v为父亲,dp[ v ]已知,那么dp[ u ]为:

    当1 - up[ u ] * e( u , v ) = 0时,dp[ u ] = 1 , 否则 dp[ u ] = merge( up[ u ] , ( dp[ v ] - up[ u ] * e( u , v ) ) / ( 1 - up[ u ] * e( u , v ) ) * e( u , v ) )。

    代码:

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <vector>
     
    using namespace std ;
     
    #define travel( x ) for ( vector < edge > :: iterator p = E[ x ].begin(  ) ; p != E[ x ].end(  ) ; ++ p )
    #define rep( i , x ) for ( int i = 0 ; i ++ < x ; )
     
    #define addedge( s , t , p ) add( s , t , p ) , add( t , s , p )
    #define add( s , t , p ) E[ s ].push_back( edge( t , p ) )
     
    const int maxn = 501000 ;
    const double esp = 0.00000001 ;
     
    struct edge {
        int t ;
        double p ; 
        edge( int _t , double _p ) : t( _t ) , p( _p ) {
        }
    } ;
    vector < edge > E[ maxn ] ;
     
    int n ; 
    double q[ maxn ] ;
     
    double up[ maxn ] , dp[ maxn ] ;
     
    double merge( double x , double y ) {
        return x + y - x * y ;
    }
     
    void dfs_up( int now , int fa ) {
        up[ now ] = q[ now ] ;
        travel( now ) if ( p -> t != fa ) {
            dfs_up( p -> t , now ) ;
            up[ now ] = merge( up[ now ] , up[ p -> t ] * p -> p ) ;
        }
    }
     
    void dfs_dp( int now , int fa ) {
        double rec , ret ; 
        travel( now ) if ( p -> t != fa ) {
            if ( ( 1 - ( rec = p -> p * up[ p -> t ] ) ) > esp ) {
                ret = ( dp[ now ] - rec ) / ( 1 - rec ) ;
                dp[ p -> t ] = merge( up[ p -> t ] , ret * p -> p ) ;
            } else dp[ p -> t ] = 1.0 ;
            dfs_dp( p -> t , now ) ;
        }
    }
     
    int main(  ) {
        scanf( "%d" , &n ) ;
        rep( i , ( n - 1 ) ) {
            int s , t ; double p ; scanf( "%d%d%lf" , &s , &t , &p ) ;
            p = p / 100.0 ;
            addedge( s , t , p ) ;
        }
        rep( i , n ) {
            scanf( "%lf" , q + i ) ;
            q[ i ] = q[ i ] / 100.0 ;
        }
        dfs_up( 1 , 0 ) ;
        dp[ 1 ] = up[ 1 ] ;
        dfs_dp( 1 , 0 ) ;
        double ans = 0 ;
        rep( i , n ) ans += dp[ i ] ;
        printf( "%.6f\n" , ans ) ;
        return 0 ; 
    }
    

    相关文章

      网友评论

        本文标题:BZOJ-3566: [SHOI2014]概率充电器(树形DP)

        本文链接:https://www.haomeiwen.com/subject/plvieqtx.html