题目大意
给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。
对于每个叶结点u,定义c[u]为从u到根结点的简单路径上第一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。
解题思路
设f[i][0,1]表示以1为根i的子树需要一个白或黑点最少要染多少个点,g[i][0,1]表示i往上的部分需要一个白或黑点最少要染多少个点,求出f和g后即可得解。
code
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define ULL unsigned long long
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const mn=1e5+9,mm=2*1e5;LL inf=1e9;
int n,m,gra,f[mn][2],g[mn][2],begin[mn],to[mm],next[mm];
void insert(int u,int v){
to[++gra]=v;
next[gra]=begin[u];
begin[u]=gra;
}
void dfs(int now,int pre){
LL tmp=f[now][0],tm2=f[now][1];
fr(i,now)if(to[i]!=pre){
dfs(to[i],now);
tmp+=min(f[to[i]][0],f[to[i]][1]+1);
tm2+=min(f[to[i]][1],f[to[i]][0]+1);
}
tmp=min(tmp,inf);
tm2=min(tm2,inf);
f[now][0]=tmp;
f[now][1]=tm2;
}
void df2(int now,int pre){
LL tmp=g[now][0],tm2=g[now][1];
fr(i,now)if(to[i]!=pre){
tmp+=min(f[to[i]][0],f[to[i]][1]+1);
tm2+=min(f[to[i]][1],f[to[i]][0]+1);
}
fr(i,now)if(to[i]!=pre){
LL tm3=tmp-min(f[to[i]][0],f[to[i]][1]+1),
tm4=tm2-min(f[to[i]][1],f[to[i]][1]+1);
g[to[i]][0]=min(tm3,inf);
g[to[i]][1]=min(tm4,inf);
df2(to[i],now);
}
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d",&m,&n);
fo(i,1,n)f[i][0]=f[i][1]=g[i][0]=g[i][1]=inf;
int x,y;
fo(i,1,n){
scanf("%d",&x);
f[i][x]=0;g[i][x]=0;
}
fo(i,1,m-1){
scanf("%d%d",&x,&y);
insert(x,y);
insert(y,x);
}
dfs(1,0);
df2(1,0);
LL ans=inf;
fo(i,1,m){
LL tmp=f[i][0]+g[i][0]+1,
tm2=f[i][1]+g[i][1]+1;
tmp=min(tmp,inf);
tm2=min(tm2,inf);
ans=min(ans,tmp);
ans=min(ans,tm2);
}
printf("%lld",ans);
return 0;
}