bzoj1864 [Zjoi2006]三色二叉树

Description

这里写图片描述

Input

仅有一行,不超过500000个字符,表示一个二叉树序列。

Output

输出文件也只有一行,包含两个数,依次表示最多和最少有多少个点能够被染成绿色。

Sample Input
1122002010

Sample Output
5 2

分析:
第一次写dp1A,✿✿ヽ(°▽°)ノ✿
如果一个节点被染成绿色,那么ta的父亲,儿子和最近兄弟都不能是绿色
也就是说,一个绿色节点最多影响4个节点,最少影响1个
设计状态
f[i][0/1/2]
当前i节点,状态是0/1/2,最多有多少个绿色节点
g[i][0/1/2]
当前i节点,状态是0/1/2,最少有多少个绿色节点

tip

转移的时候耐下心来慢慢写就好
(瞎扯,ctrl+c和ctrl+v就好)

觉得唯一有点难度的就是怎么把序列还原成树的形态
dfs,记一个bh(常函数)
bh只加不减,表示序列中的指针右移

这里写代码片
#include<cstdio>
#include<iostream>
#include<cstring>

using namespace std;

const int N=500001;
char s[N];
int f[N][3],g[N][3],fa[N<<1],bh,ch[N][2];

void dfs(int faa)
{
    bh++;  //当前位置 
    ch[faa][ch[faa][0] ? 1:0]=bh;
    fa[bh]=faa;
    int x=s[bh-1]-'0';
    int r=bh;  //局部变量,保证儿子能精准的找到爸爸
    if (x==2)  //两个儿子 
    {
        dfs(r);
        dfs(r);
    }
    else if (x==1)
    {
        dfs(r);
    }
    else return;
}

void doit(int now)
{
    if (ch[now][0]) doit(ch[now][0]);   //dp son
    if (ch[now][1]) doit(ch[now][1]);
    if (!ch[now][0]&&!ch[now][1])  //leave
    {
        f[now][0]=0;f[now][1]=0;f[now][2]=1;  //two means G
        g[now][0]=0;g[now][1]=0;g[now][2]=1;  //two means G
        return;
    }
    else if (ch[now][0]&&ch[now][1])  //two sons
    {
        f[now][0]=max(f[ch[now][0]][2]+f[ch[now][1]][1],f[ch[now][0]][1]+f[ch[now][1]][2]);
        f[now][1]=max(f[ch[now][0]][2]+f[ch[now][1]][0],f[ch[now][0]][0]+f[ch[now][1]][2]);
        f[now][2]=max(f[ch[now][0]][0]+f[ch[now][1]][1],f[ch[now][0]][1]+f[ch[now][1]][0])+1;
        g[now][0]=min(g[ch[now][0]][2]+g[ch[now][1]][1],g[ch[now][0]][1]+g[ch[now][1]][2]);
        g[now][1]=min(g[ch[now][0]][2]+g[ch[now][1]][0],g[ch[now][0]][0]+g[ch[now][1]][2]);
        g[now][2]=min(g[ch[now][0]][0]+g[ch[now][1]][1],g[ch[now][0]][1]+g[ch[now][1]][0])+1;
    }
    else if (ch[now][0])
    {
        f[now][0]=max(f[ch[now][0]][2],f[ch[now][0]][1]);
        f[now][1]=max(f[ch[now][0]][2],f[ch[now][0]][0]);
        f[now][2]=max(f[ch[now][0]][0],f[ch[now][0]][1])+1;
        g[now][0]=min(g[ch[now][0]][2],g[ch[now][0]][1]);
        g[now][1]=min(g[ch[now][0]][2],g[ch[now][0]][0]);
        g[now][2]=min(g[ch[now][0]][0],g[ch[now][0]][1])+1;
    }
    else
    {
        f[now][0]=max(f[ch[now][1]][2],f[ch[now][1]][1]);
        f[now][1]=max(f[ch[now][2]][2],f[ch[now][1]][0]);
        f[now][2]=max(f[ch[now][2]][0],f[ch[now][1]][1])+1;
        g[now][0]=min(g[ch[now][1]][2],g[ch[now][1]][1]);
        g[now][1]=min(g[ch[now][2]][2],g[ch[now][1]][0]);
        g[now][2]=min(g[ch[now][2]][0],g[ch[now][1]][1])+1;
    }
}

int main()
{
    scanf("%s",&s);
    bh=0;
    dfs(0);   //还原树的形态 
    doit(1);
    int f1=max(f[1][0],max(f[1][1],f[1][2]));
    int f2=min(g[1][0],min(g[1][1],g[1][2]));
    printf("%d %d",f1,f2);
    return 0;
}

转载于:https://www.cnblogs.com/wutongtong3117/p/7673461.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值