Description
Input
仅有一行,不超过500000个字符,表示一个二叉树序列。
Output
输出文件也只有一行,包含两个数,依次表示最多和最少有多少个点能够被染成绿色。
Sample Input
Sample Output
题解Here!
很明显,一道树形$DP$。
首先,那个序列转换成树,应该没有什么问题,一发$DFS$搞定。
然后就是在树上$DP$。
那个最大最小值先不管它,对于一种情况讨论,另外一种情况同理即可。
先看最大值:
设$dp[i][0/1/2]$表示对于$i$的子树,$i$涂绿色$0$,红色$1$,蓝色$2$,所能达到的最多的绿色节点个数。
然后对于$i$的儿子个数$num[i]$开始特判:
- $num[i]==0$:
这个说明$i$为叶子节点,直接$dp[i][0]=1;dp[i][1]=dp[i][2]=0;$即可。
- $num[i]==1$:
只有一个儿子也很好办,分类讨论儿子涂什么颜色即可:
$$dp[i][0]=\max\left\{\begin{array}{}dp[j][1]+1\\dp[j][2]+1\end{array}\right.j\in son_i$$
$$dp[i][1/2]=\max\left\{\begin{array}{}dp[j][0]\\dp[j][2/1]\end{array}\right.j\in son_i$$
注:斜杠表示两种方案只选一种,并且不同的两个斜杠表示的四个决策是两两对应的。
- $num[i]==2$:
额,这个就稍微有点烦人了,但是还是很好想:
$$dp[i][0]=\max\left\{\begin{array}{}\max\left\{\begin{array}{}dp[j_1][1]+1\\dp[j_2][2]+1\end{array}\right.\\\\\max\left\{\begin{array}{}dp[j_1][2]+1\\dp[j_2][1]+1\end{array}\right.\end{array}\right.$$
$$dp[i][1/2]=\max\left\{\begin{array}{}\max\left\{\begin{array}{}dp[j_1][0]\\dp[j_2][2/1]\end{array}\right.\\\\\max\left\{\begin{array}{}dp[j_1][2/1]\\dp[j_2][0]\end{array}\right.\end{array}\right.$$
其中$j_1,j_2\in son_i$。
然后就是$DFS$一遍就可以出解了。
最后答案就是:$$\max\left\{\begin{array}{}dp[1][0]\\dp[1][1]\\dp[1][2]\end{array}\right.$$
这只对于最大值,最小值同理。
记得最小值开始时要赋为$+\infty$。
本蒟蒻表示$1A$很高兴!
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 500010
using namespace std;
int n=1,c=1,pos=1;
int head[MAXN],num[MAXN],f[MAXN][3],g[MAXN][3];
struct Tree{
int next,to;
}a[MAXN<<1];
char str[MAXN];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline void add(int x,int y){
a[c].to=y;a[c].next=head[x];head[x]=c++;
}
void dfs(int rt){
num[rt]=str[pos]-'0';
for(int i=1;i<=num[rt];i++){
n++;
add(rt,n);
pos++;
dfs(n);
}
}
void dp(int rt){
if(num[rt]==0){
f[rt][0]=g[rt][0]=1;
f[rt][1]=f[rt][2]=g[rt][1]=g[rt][2]=0;
}
else if(num[rt]==1){
int will;
for(int i=head[rt];i;i=a[i].next){
will=a[i].to;
dp(will);
f[rt][0]=max(f[rt][0],max(f[will][1],f[will][2])+1);
f[rt][1]=max(f[rt][1],max(f[will][0],f[will][2]));
f[rt][2]=max(f[rt][2],max(f[will][0],f[will][1]));
g[rt][0]=min(g[rt][0],min(g[will][1],g[will][2])+1);
g[rt][1]=min(g[rt][1],min(g[will][0],g[will][2]));
g[rt][2]=min(g[rt][2],min(g[will][0],g[will][1]));
}
}
else{
int will,son[2];
for(int i=head[rt],j=0;i;i=a[i].next,j++){
will=a[i].to;
son[j]=will;
dp(will);
}
f[rt][0]=max(f[rt][0],max(f[son[0]][1]+f[son[1]][2],f[son[0]][2]+f[son[1]][1])+1);
f[rt][1]=max(f[rt][1],max(f[son[0]][0]+f[son[1]][2],f[son[0]][2]+f[son[1]][0]));
f[rt][2]=max(f[rt][2],max(f[son[0]][0]+f[son[1]][1],f[son[0]][1]+f[son[1]][0]));
g[rt][0]=min(g[rt][0],min(g[son[0]][1]+g[son[1]][2],g[son[0]][2]+g[son[1]][1])+1);
g[rt][1]=min(g[rt][1],min(g[son[0]][0]+g[son[1]][2],g[son[0]][2]+g[son[1]][0]));
g[rt][2]=min(g[rt][2],min(g[son[0]][0]+g[son[1]][1],g[son[0]][1]+g[son[1]][0]));
}
}
void work(){
scanf("%s",str+1);
dfs(1);
memset(f,0,sizeof(f));
memset(g,127,sizeof(g));
dp(1);
printf("%d %d\n",max(f[1][0],max(f[1][1],f[1][2])),min(g[1][0],min(g[1][1],g[1][2])));
}
int main(){
work();
return 0;
}