Description
给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。
对于每个叶结点u,定义 c [ u ] c[u] c[u] 为从 u u u 到根结点的简单路径上第一个有色结点的颜色。给出每个 c [ u ] c[u] c[u] 的值,设计着色方案,使得着色结点的个数尽量少。
Solution
先给出一个结论:对于任意一个可行根,对答案没有影响。
- 证明:设当前根为 x x x,有一与其相连的 y y y 。若 x x x 和 y y y 都有颜色,则可以得出 x x x 和 y y y 颜色必然不同(否则就不是最优解),因此根从 x x x 到 y y y 不会对答案有影响。
那么根据这个结论,不妨随便找一个根。
考虑树形 d p dp dp。设 f i , 0 / 1 / 2 f_{i,0/1/2} fi,0/1/2 表示当前节点 i i i 涂 0,1 或者不涂。
先考虑有颜色的。转移分为叶子结点和非叶节点。
对于 f i , 0 f_{i,0} fi,0,如果 s o n son son 是叶子节点并且要 1 ,那么 f i , 0 + 1 f_{i,0}+1 fi,0+1。如果是非叶节点, f i , 0 = ∑ min ( min ( f s o n , 0 − 1 , f s o n , 1 ) , f s o n , 2 ) f_{i,0}=\sum\min(\min(f_{son,0}-1,f_{son,1}),f_{son,2}) fi,0=∑min(min(fson,0−1,fson,1),fson,2)。
对于 f i , 1 f_{i,1} fi,1 同理。
而对于 f i , 2 f_{i,2} fi,2 则没有那么多顾及, f i , 2 = ∑ min ( min ( f s o n , 0 , f s o n , 1 ) , f s o n , 2 ) f_{i,2}=\sum\min(\min(f_{son,0},f_{son,1}),f_{son,2}) fi,2=∑min(min(fson,0,fson,1),fson,2)。
初值:对于叶子结点, f i , c i = 1 , f i , 1 − c i = f i , 2 = inf f_{i,c_i}=1,f_{i,1-c_i}=f_{i,2}=\inf fi,ci=1,fi,1−ci=fi,2=inf,对于非叶节点 f i , 0 / 1 = 1 f_{i,0/1}=1 fi,0/1=1。
Code
#include<cstdio>
#include<algorithm>
#define N 100005
#define inf 123456789
using namespace std;
struct node
{
int to,next,head;
}a[N<<1];
int n,m,rt,x,y,tot,c[N],f[N][3];
void add(int x,int y) {a[++tot].to=y;a[tot].next=a[x].head;a[x].head=tot;}
void dfs(int x,int fa)
{
if (x>m) f[x][0]=f[x][1]=1;
for (int i=a[x].head;i;i=a[i].next)
{
int y=a[i].to;
if (y==fa) continue;
dfs(y,x);
if (y<=m) {if (c[y]==1) f[x][0]++;}
else f[x][0]+=min(min(f[y][0]-1,f[y][1]),f[y][2]);
if (y<=m) {if (c[y]==0) f[x][1]++;}
else f[x][1]+=min(min(f[y][1]-1,f[y][0]),f[y][2]);
f[x][2]+=min(min(f[y][0],f[y][1]),f[y][2]);
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i)
scanf("%d",&c[i]),f[i][c[i]]=1,f[i][c[i]^1]=f[i][2]=inf;
rt=m+1;
for (int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs(rt,0);
printf("%d\n",min(min(f[rt][0],f[rt][1]),f[rt][2]));
return 0;
}