【CQOI2009】叶子的染色

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,01,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,1ci=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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值