题目链接:CodeVS1218
题目大意
大小为
N
的树上有
分析
1. 对于所有的军队,越往上,显然能封锁的节点就越多;时间越多,能往上跑的距离就越多;所以二分时间。
2. 有一些军队,可以跨过根节点而控制根节点的其他子节点( 以下简称为子节点 )。
3. 对于不能跨根节点的军队( 包括不能到根和刚好能到根 ),直接在停止处标记;而那些可以跨根的军队,要记录起来( 记为
X
)最后一起匹配。
4. 对于子节点,其是否需要从跟节点调用一支军队来控制可以通过一次dfs搜索判断;需要军队的记录下来( 记为
5. 先把
X,Y
排序,对当前
X
的标记
6. 然而,当前的
X[i]
一定是剩余的军队中最没用的,所以如果它自己的子节点没有被控制,就去控制自己的节点,否则就去控制当先匹配到的
Y[j]
(如果这个子节点已经被某个军队跑回来自己控制,那么就后移
j
<script type="math/tex" id="MathJax-Element-14">j</script> 标记) 。
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 ;
}
以上