[NOIP2012]疫情控制

来源:互联网 发布:牛仔衬衫 知乎 编辑:程序博客网 时间:2024/05/02 01:09

题目链接:CodeVS1218

题目大意
大小为 N 的树上有M个可移动的军队,问至少需要多少时间,可以封锁这棵树(即从根节点开始不经过这些点驻扎的节点而到达叶子节点),军队在树上移动的时间即为树的边权。(N,M50000)

分析
1. 对于所有的军队,越往上,显然能封锁的节点就越多;时间越多,能往上跑的距离就越多;所以二分时间。
2. 有一些军队,可以跨过根节点而控制根节点的其他子节点( 以下简称为子节点 )。
3. 对于不能跨根节点的军队( 包括不能到根和刚好能到根 ),直接在停止处标记;而那些可以跨根的军队,要记录起来( 记为 X )最后一起匹配。
4. 对于子节点,其是否需要从跟节点调用一支军队来控制可以通过一次dfs搜索判断;需要军队的记录下来( 记为 Y )。
5. 先把X,Y 排序,对当前 X 的标记i和对当前Y 的标记j不断后移。对于每一个 X[i] 有两种决策:控制自己的的来源或者控制另外的子节点。
6. 然而,当前的X[i] 一定是剩余的军队中最没用的,所以如果它自己的子节点没有被控制,就去控制自己的节点,否则就去控制当先匹配到的Y[j](如果这个子节点已经被某个军队跑回来自己控制,那么就后移j 标记) 。
6.5 本人当时用了一个非常低效的匹配方法,导致无限T,因此开了一些优化,比如倍增(其实不倍增并不会慢多少)。

上代码

#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std ;const int N = 5e4 + 10 ;const int M = 30 + 10 ;int n, m, tot ;struct node_edge {    int to, val ;} ;vector < node_edge > edge[ N ] ;struct node_army { // 记录军队    int plc, time, nown ; // 起始位置,剩余时限,目前位置    // 其中plc是不变的,而time和nown是变化的} army[ N ] ;// dfs一棵树// fa[i][j]表示i的 2^j 祖先,所以fa[i][0]即为i的父亲int fa[ N ][ M ], dist[ N ] ; void dfs( int a , int b ) {    fa[ a ][ 0 ] = b ;    for ( int i = 0 ; i < edge[ a ].size() ; i ++ ) {        int node = edge[ a ][ i ].to ;        if ( node == b )    continue ;        dist[ node ] = dist[ a ] + edge[ a ][ i ].val ;        dfs( node , a ) ;    }}// 为了优化,我连倍增都用了,当时无限T的心情可以脑补// 树上倍增inline void calc_fa() {    for ( int j = 1 ; ; j ++ ) {        int flag = false ;        for ( int i = 1 ; i <= n ; i ++ ) {            int node = fa[ i ][ j - 1 ] ;            if ( !fa[ node ][ j - 1 ] ) continue ;            flag = true ; fa[ i ][ j ] = fa[ node ][ j - 1 ] ;        }        if ( !flag )    return ;    }}// 读入优化,说多了都是泪inline void get_num( int &a ) {    char ch ;    while ( ch = getchar(), ch >= '0' && ch <= '9' )        a = a * 10 + ch - '0' ;    return ;}inline void init() {    scanf( "%d", &n ) ; getchar() ;    for ( int i = 1 ; i < n ; i ++ ) {        int a = 0, b = 0, c = 0 ;        get_num( a ), get_num( b ), get_num( c ) ;        edge[ a ].push_back( (node_edge){ b , c } ) ;        edge[ b ].push_back( (node_edge){ a , c } ) ;        tot += c ;     }    dfs( 1 , 0 ), calc_fa() ;    scanf( "%d", &m ) ; getchar() ;    for ( int i = 1 ; i <= m ; i ++ )         get_num( army[ i ].plc ) ;}bool book[ N ] ; // 封锁标记int lx, ly ;struct node_xxyy {    int from, val ;    // 对X为军队的来源和剩余时间    // 对Y为子节点和距离    inline bool operator < ( const node_xxyy a ) const {        return val < a.val ;    }} x[ N ], y[ N ] ;inline void go_up( int a ) {    int node = army[ a ].nown ;    while ( fa[ node ][ 0 ] != 1 ) {        int k = 0 ;        while ( fa[ node ][ k ] != 1 && fa[ node ][ k ] != 0 ) {            if ( dist[ node ] - dist[ fa[ node ][ k ] ] <= army[ a ].time )                k ++ ;            else    break ;        }        if ( k == 0 ) {            book[ army[ a ].nown = node ] = true ; return ;        } else {            army[ a ].time -= dist[ node ] - dist[ fa[ node ][ k - 1 ] ] ;            army[ a ].nown = node = fa[ node ][ k - 1 ] ;        }    }    if ( army[ a ].time <= dist[ army[ a ].nown ] )        book[ army[ a ].nown ] = true ;    return ;}bool check( int a ) {    if ( book[ a ] )    return true ;    for ( int i = 0 ; i < edge[ a ].size() ; i ++ ) {        int node = edge[ a ][ i ].to ;        if ( node == fa[ a ][ 0 ] ) continue ;        if ( !check( node ) )   return false ;    }    if ( edge[ a ].size() > 1 ) {        book[ a ] = true ; return true ; //优化之一    } else         return false ;}inline bool judge( int valn ) {    for ( int i = 1 ; i <= m ; i ++ )        army[ i ].time = valn, army[ i ].nown = army[ i ].plc ;    for ( int i = 1 ; i <= m ; i ++ ) {        go_up( i ) ;        if ( army[ i ].time > dist[ army[ i ].nown ] ) // 说明可以越根            x[ ++ lx ] = (node_xxyy){ army[ i ].nown , army[ i ].time - dist[ army[ i ].nown ] } ;    }    for ( int i = 0 ; i < edge[ 1 ].size() ; i ++ )        if ( !check( edge[ 1 ][ i ].to ) )              y[ ++ ly ] = (node_xxyy){ edge[ 1 ][ i ].to , dist[ edge[ 1 ][ i ].to ] } ;    if ( lx < ly )  return false ; // 剪枝    sort( x + 1 , x + lx + 1 ) ; sort( y + 1 , y + ly + 1 ) ;    int mark = 1 ;    for ( int i = 1 ; i <= lx ; i ++ ) { //我T就是T在这个匹配,我原来的版本太low了:在Y中upper_bound()        if ( !book[ x[ i ].from ] ) {            book[ x[ i ].from ] = true ; continue ;        } else {            while ( book[ y[ mark ].from ] && mark < ly )   mark ++ ;            if ( x[ i ].val >= y[ mark ].val ) {                book[ y[ mark ].from ] = true ;                mark ++ ;            }        }    }    for ( int i = 1 ; i <= ly ; i ++ )        if ( !check( y[ i ].from ) )    return false ;    return true ;}inline int figure() {    if ( m < edge[ 1 ].size() ) return -1 ; // 唯一无法封锁的情况    int ans ;    int l = 1, r = tot, mid ;    while ( l <= r ) {        lx = ly = 0 ;        memset( book , 0 , sizeof( book ) ) ;        mid = ( l + r ) >> 1 ;        if ( judge( mid ) )            r = mid - 1, ans = mid ;        else            l = mid + 1 ;    }    return ans ;}int main() {    init() ;    printf( "%d\n", figure() ) ;    return 0 ;}

以上

0 0
原创粉丝点击