题目大意
给定一个n个节点的树,其中有一些节点是关键点。
现在将树划分为若干个连通块,要求每个块内都至少有一个关键点,求最大的连通块最小是多大。
Data Constraint
题解
最大值最小化,考虑二分。
现在的问题是如何判定。
设fi表示i所在的连通块大小至少为
然后可以贪心地转移。
如果当前点是关键点,那么
然后每个点,如果fi<gi那多出来的gi−fi就可能被它的父亲用到。
否则就还需要它的父亲来包括他。
时间复杂度:O(nlogn)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 200000 + 10
bool flag ;
int Node[2*N] , Next[2*N] , Head[N] , tot ;
int f[N] , g[N] , Col[N] ;
int n , m , ans ;
void link( int u , int v ) {
Node[++tot] = v ;
Next[tot] = Head[u] ;
Head[u] = tot ;
}
void DFS( int x , int lim , int F ) {
f[x] = g[x] = 0 ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( Node[p] == F ) continue ;
DFS( Node[p] , lim , x ) ;
}
if ( Col[x] ) g[x] = lim - 1 ;
else f[x] ++ ;
if ( f[x] > lim ) flag = 0 ;
if ( f[x] <= g[x] ) g[F] = max( g[F] , g[x] - f[x] ) ;
else f[F] += f[x] ;
}
bool Check( int st ) {
flag = 1 ;
DFS( 1 , st , 0 ) ;
if ( f[1] > g[1] ) flag = 0 ;
return flag ;
}
int main() {
freopen( "deep.in" , "r" , stdin ) ;
freopen( "deep.out" , "w" , stdout ) ;
scanf( "%d%d" , &n , &m ) ;
for (int i = 1 ; i < n ; i ++ ) {
int u , v ;
scanf( "%d%d" , &u , &v ) ;
link( u , v ) ;
link( v , u ) ;
}
for (int i = 1 ; i <= m ; i ++ ) {
int x ;
scanf( "%d" , &x ) ;
Col[x] = 1 ;
}
int l = 1 , r = n ;
while ( l <= r ) {
int mid = (l + r) / 2 ;
if ( Check(mid) ) r = mid - 1 , ans = mid ;
else l = mid + 1 ;
}
printf( "%d\n" , ans ) ;
return 0 ;
}
以上.