[NOIP2012]疫情控制

題目鏈接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 ;
}

以上

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章