BZOJ1304: [CQOI2009]叶子的染色 树形dp

时间:2023-12-01 20:53:44

Description

给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。 对于每个叶结点u,定义c[u]为从根结点从U的简单路径上最后一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。

Input

第一行包含两个正整数m, n,其中n是叶子的个数,m是结点总数。结点编号为1,2,…,m,其中编号1,2,… ,n是叶子。以下n行每行一个0或1的整数(0表示黑色,1表示白色),依次为c[1],c[2],…,c[n]。以下m-1行每行两个整数a,b(1<=a < b <= m),表示结点a和b 有边相连。

Output

仅一个数,即着色结点数的最小值。

Sample Input

5 3
0
1
0
1 4
2 5
4 5
3 5

Sample Output

2

HINT

M<=10000

N<=5021

Solution

这题只要知道结论就很好做了

然而是神仙结论:选择任何一个点为根对答案没有任何影响(不会证)

所以直接随便选个点当根然后树形dp就可以了

$f[u][0]$和$f[u][1]$表示$u$的子树中,最后一个点想要得到一个白色/黑色的祖先,的最小代价

#include <bits/stdc++.h>

using namespace std ;

#define N 100010
#define inf 0x3f3f3f3f int n , m ;
int c[ N ] ;
int f[ N ][ ] ;
int head[ N ] , cnt ;
int fa[ N ] ;
struct node {
int to , nxt ;
} e[ N ] ; void ins( int u , int v ) {
e[ ++ cnt ].to = v ;
e[ cnt ].nxt = head[ u ] ;
head[ u ] = cnt ;
} void dfs( int u ) {
if( u <= n ) {
f[ u ][ c[ u ] ] = ;
f[ u ][ c[ u ] ^ ] = inf ;
}
for( int i = head[ u ] ; i ; i = e[ i ].nxt ) {
if( e[ i ].to == fa[ u ] ) continue ;
fa[ e[ i ].to ] = u ;
dfs( e[ i ].to ) ;
f[ u ][ ] += min( f[ e[ i ].to ][ ] , f[ e[ i ].to ][ ] + ) ;
f[ u ][ ] += min( f[ e[ i ].to ][ ] + , f[ e[ i ].to ][ ] ) ;
}
} int main() {
scanf( "%d%d" , &m , &n ) ;
for( int i = ; i <= n ; i ++ ) {
scanf( "%d" , &c[ i ] ) ;
}
for( int i = , a , b ; i < m ; i ++ ) {
scanf( "%d%d" , &a , &b ) ;
ins( a , b ) ; ins( b , a ) ;
}
dfs( m ) ;
printf( "%d\n" , min( f[ m ][ ] , f[ m ][ ] ) + ) ;
return ;
}