【jzoj1010】【CQOI2009】【叶子的颜色】【树型动态规划】

题目大意

 给一棵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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值