1. 程式人生 > >BZOJ1864: [Zjoi2006]三色二叉樹

BZOJ1864: [Zjoi2006]三色二叉樹

BZOJ1864: [Zjoi2006]三色二叉樹

Description

Input

僅有一行,不超過500000個字元,表示一個二叉樹序列。

Output

輸出檔案也只有一行,包含兩個數,依次表示最多和最少有多少個點能夠被染成綠色。

Sample Input

1122002010

Sample Output

5 2

題解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;
}