P2585 [ZJOI2006]三色二叉树

(文章目录)


前言

典型的树上dp。


题目大意

你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。

对于全部的测试点,保证 1s5×1051 \leq |s| \leq 5 \times 10^5ss 中只含字符 0 1 2


思路

由于有三种颜色,我们可以把它建模成 dp[u][0/1/2]dp[u][0/1/2],顺序其实无所谓,这里用 00 当绿了。这个数组也就是在 uu 这个结点颜色为绿/红/蓝的三种颜色的绿色结点最大/最小值。

由于不能跟子节点一样,所以自己当前的颜色 dp[u][k]dp[u][k] 就要从不是 kk 的另外两个数过来。特别的,如果这个颜色为绿,就要 +1+1
所以就差不多是这样:

dp[u][0]=max(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;dp[u][0]= max(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;

dp[u][1]=max(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);dp[u][1]= max(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);

dp[u][2]=max(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);dp[u][2]= max(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);

建树的部分就递归建树就好,max\maxmin\min 可以用两个函数解决,第二次的时候清空就行了。之后由于根可以有三个颜色,答案应该在三个颜色的其中。这个看起来更好理解,其实用绿和红/蓝就行。


代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll MAXN=5e5+5;
ll dp[MAXN][4],sz;
char a[MAXN];
struct node{
    ll l,r;
}t[MAXN];
ll build(){
    ll u=++sz;
    if(a[sz]=='2'){
        t[u].l=build();
        t[u].r=build();
    }else if(a[sz]=='1'){
        t[u].l=build();
    }
    return u;
}
void dp1(ll u){
    if(t[u].l==0&&t[u].r==0){
        dp[u][0]=1;
        return;
    }else if(t[u].l&&t[u].r){
        dp1(t[u].l);
        dp1(t[u].r);
    }else{
        dp1(t[u].l);
    }
    dp[u][0]= max(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;
    dp[u][1]= max(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);
    dp[u][2]= max(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);
}

void dp2(ll u){
    if(t[u].l==0&&t[u].r==0){
        dp[u][0]=1;
        return;
    }else if(t[u].l&&t[u].r){
        dp2(t[u].l);
        dp2(t[u].r);
    }else{
        dp2(t[u].l);
    }
    dp[u][0]= min(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;
    dp[u][1]= min(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);
    dp[u][2]= min(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);
}
int main(){
    scanf("%s",(a+1));
    build();
    dp1(1);
    printf("%lld ", max({dp[1][0],dp[1][1],dp[1][2]}));
    memset(dp,0,sizeof(dp));
    dp2(1);
    printf("%lld ", min({dp[1][0],dp[1][1],dp[1][2]}));
    return 0;
}

总结

这个问题可以归纳为树上多情况dp,也就是每一个位置的状态不止有一个,而是有多个。比如这题是红蓝绿。