20210524考试—树 题解
两种做法,树形\(dp\)或者高斯消元
树形\(dp\),其实我考场上想这种做法了,当时认为应该跟小胖守皇宫的状态差不多,就设计了个被谁点亮,结果咋也推不出来式子。
实际上这道题并不关心他是被谁点亮的,我们只需要关心他当前按没按,亮不亮就可以了,我们可以设计出来\(4\)种状态,分别对应按了亮了,按了没亮,没按亮了,没按没亮。
然后式子很好推。
就是在取最值的时候有一个技巧,这个也是树形\(dp\)中常用的。
我们想知道子节点中有偶数个亮的时候和奇数个亮的时候的最小值,这个完全没有必要一个一个枚举。
只需要对每一个子节点都直接要最小值,那么取出来一定是一种情况的答案,只需要记录一个最小的差值,补上就行了
多测不清空,爆零两行泪
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <bitset>
#define printf Ruusupuu=printf
#define R register int
#define int long long
using namespace std ;
const int N = 3e2 + 10 ;
const int Inf = 0x3f3f3f3f ;
typedef long long L ;
typedef long double D ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
int Ruusupuu , n , x , y , cnt , head [N] , ans , f [N][3] ;
struct E{ int fr , to , next ; } a [N << 1] ;
//0 -> 按,亮 1 -> 不按,亮 2 -> 不按,不亮
inline void add( int f , int t ){
a [++ cnt].fr = f ;
a [cnt].to = t ;
a [cnt].next = head [f] ;
head [f] = cnt ;
}
inline void clear(){
memset( head , -1 , sizeof( head ) ) ;
memset( a , 0 , sizeof( a ) ) ;
memset( f , 0 , sizeof( f ) ) ;
cnt = 0 ;
}
void dfs( int x , int fa ){
int ans = 0 , minn = Inf , t = 0 ;
for( R i = head [x] ; ~i ; i = a [i].next ){
int y = a [i].to ;
if( y == fa ) continue ;
dfs( y , x ) ;
f [x][0] += f [y][2] ;
minn = min ( minn , abs( f [y][0] - f [y][1] ) ) ;
if( f [y][0] > f [y][1] ) ans += f [y][1] ;
else ans += f [y][0] , t ++ ;
} f [x][0] ++ ;
if( t & 1 ) f [x][1] = ans , f [x][2] = ans + minn ;
else f [x][1] = ans + minn , f [x][2] = ans ;
}
void sc(){
while( scanf( "%lld" , &n ) && n != 0 ){
clear() ;
for( R i = 1 ; i < n ; i ++ ) x = read() , y = read() , add( x , y ) , add( y , x ) ;
dfs( 1 , 0 ) ;
printf( "%lld\n" , min( f [1][0] , f [1][1] ) ) ;
}
}
signed main(){
sc() ;
return 0 ;
}
高斯消元先咕咕。
$The \ light \ has \ betrayed \ me$

浙公网安备 33010602011771号