解题报告:luogu P2585
题目链接:P2585 [ZJOI2006]三色二叉树
_shy 大佬求助,就尝试切了切。
树形计数 \(dp\) ,比树上背包简单多了。
设 \(dp_{i,0/1/2}\) 为 \(i\) 号点颜色为绿,红,蓝时绿色的个数,然后处理出每个节点的儿子数(我可能做麻烦了),分类讨论即可,方程太多,就看代码吧。
注意初始化。
其实可以直接在序列上算,然而我不熟,就先转化成图再做的。
\(Code\):
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 500005
#define inf 2147483647
char c[MAXN];
int t;
struct node
{
int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;
int deg[MAXN];
int dp[MAXN][3];
int son1[MAXN],son2[MAXN];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
return;
}
void join(int u,int v){add(u,v),add(v,u);}
int dfs1(int cur)
{
if(c[cur]=='1'){join(cur,cur+1);return dfs1(cur+1);}
if(c[cur]=='0') return cur;
else
{
join(cur,cur+1);
int now=dfs1(cur+1)+1;
join(cur,now);
return dfs1(now);
}
}
void dfs2(int cur,int fa)
{
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(j==fa) continue;
else dfs2(j,cur),deg[cur]++;
}
return;
}
void dfs(int cur,int fa)
{
son1[cur]=0,son2[cur]=0;
if(!deg[cur]){dp[cur][0]=1,dp[cur][1]=dp[cur][2]=0;return;}
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(j==fa) continue;
if(!son1[cur]) son1[cur]=j;
else son2[cur]=j;
dfs(j,cur);
}
if(deg[cur]==1)
{
dp[cur][0]=max(dp[son1[cur]][1],dp[son1[cur]][2])+1;
dp[cur][1]=max(dp[son1[cur]][0],dp[son1[cur]][2]);
dp[cur][2]=max(dp[son1[cur]][0],dp[son1[cur]][1]);
}
else if(deg[cur]==2)
{
dp[cur][0]=max(dp[son1[cur]][1]+dp[son2[cur]][2],dp[son1[cur]][2]+dp[son2[cur]][1])+1;
dp[cur][1]=max(dp[son1[cur]][0]+dp[son2[cur]][2],dp[son1[cur]][2]+dp[son2[cur]][0]);
dp[cur][2]=max(dp[son1[cur]][0]+dp[son2[cur]][1],dp[son1[cur]][1]+dp[son2[cur]][0]);
}
return;
}
void dfss(int cur,int fa)
{
if(!deg[cur]){dp[cur][0]=1,dp[cur][1]=dp[cur][2]=0;return;}
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(j==fa) continue;
dfss(j,cur);
}
if(deg[cur]==1)
{
dp[cur][0]=min(dp[son1[cur]][1],dp[son1[cur]][2])+1;
dp[cur][1]=min(dp[son1[cur]][0],dp[son1[cur]][2]);
dp[cur][2]=min(dp[son1[cur]][0],dp[son1[cur]][1]);
}
else if(deg[cur]==2)
{
dp[cur][0]=min(dp[son1[cur]][1]+dp[son2[cur]][2],dp[son1[cur]][2]+dp[son2[cur]][1])+1;
dp[cur][1]=min(dp[son1[cur]][0]+dp[son2[cur]][2],dp[son1[cur]][2]+dp[son2[cur]][0]);
dp[cur][2]=min(dp[son1[cur]][0]+dp[son2[cur]][1],dp[son1[cur]][1]+dp[son2[cur]][0]);
}
return;
}
int main()
{
scanf("%s",c);
int len=strlen(c);
memset(dp,0,sizeof(dp));
dfs1(0);
dfs2(0,0);
dfs(0,0);
printf("%d ",max(dp[0][0],max(dp[0][1],dp[0][2])));
for(int i=0;i<len;i++) dp[i][0]=dp[i][1]=dp[i][2]=inf;
dfss(0,0);
printf("%d\n",min(dp[0][0],min(dp[0][1],dp[0][2])));
return 0;
}