[CQOI2009]叶子的染色

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 730  Solved: 485
[Submit][Status][Discuss]

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

先随便选一个根。

dp[x][0/1]表示以x为根的子树中,x节点被染成黑/白色,并且这棵子树中的叶子都被满足的最小染色数。

然后发现如果x的一个儿子和它同色,那么儿子是不必要染色的(既然有的叶子到根的第一个有色节点是x的儿子,

那么如果把x的儿子替换成x也是ojbk的)

但是异色肯定是要染的。
所以dp[x][i]=∑min(dp[son][i]-1,dp[son][i^1])。
 
然后惊奇的发现换根对答案并没有什么影响,加入把root的某个儿子son旋上来,那么原来到son的现在还是到son,
son子树外的还是到root(或者root的某个异色的儿子),所以O(N)随便选个不是叶子的当根算一次就好了。
 
/**************************************************************
    Problem: 1304
    User: JYYHH
    Language: C++
    Result: Accepted
    Time:60 ms
    Memory:1660 kb
****************************************************************/
 
#include<bits/stdc++.h>
#define ll long long
#define maxn 10005
#define pb push_back
using namespace std;
vector<int> g[maxn];
int dp[maxn][2],col[maxn];
int n,m;
 
void dfs(int x,int fa){
    if(x<=m) return;
    int to; dp[x][0]=dp[x][1]=1;
    for(int i=g[x].size()-1;i>=0;i--){
        to=g[x][i];
        if(to==fa) continue;
        dfs(to,x);
         
        dp[x][0]+=min(dp[to][1],dp[to][0]-1);
        dp[x][1]+=min(dp[to][0],dp[to][1]-1);
    }
}
 
int main(){
    memset(dp,0x3f,sizeof(dp));
     
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d",col+i);
        dp[i][col[i]]=1;
    }
     
    int uu,vv;
    for(int i=1;i<n;i++){
        scanf("%d%d",&uu,&vv);
        g[uu].pb(vv),g[vv].pb(uu);
    }
     
    dfs(n,n);
    printf("%d
",min(dp[n][0],dp[n][1]));
    return 0;
}
原文地址:https://www.cnblogs.com/JYYHH/p/8300906.html