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

题目: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 
#include 
#include 
#include 
 
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))