orz 十分钟切掉的 M_sea 巨佬
蒟蒻用的是一种比较蠢的状态。。。
\(0\) 表示绿色
设 \(dp[i,color]\) 为第 \(i\) 个点被标成 \(color\) 的子树最优解。
然后转移的时候只要枚举一下儿子的颜色即可。
方程懒得写了。。。
总而言之还是 \(O(n)\) 的辣,但是常数巨大
#include <iostream>
#include <cstdio>
#include <cstring>
const int max_n = 500000 + 5;
const int inf = 0x3f3f3f3f;
int N, Tot = 1;
int Ch[2][max_n << 1], Dp1[3][max_n << 1], Dp2[3][max_n << 1];
char A[max_n];
void build(int x, int y)
{
Ch[x][y] = ++Tot;
if(A[Tot] == ‘0‘) return;
if(A[Tot] == ‘1‘) build(0, Ch[x][y]);
else
{
build(0, Ch[x][y]);
build(1, Ch[x][y]);
}
}
int main()
{
scanf("%s", &A[1]);
if(A[1] == ‘1‘) build(0, 1);
else if(A[1] == ‘2‘)
{
build(0, 1);
build(1, 1);
}
memset(Dp2, inf, sizeof(Dp2));
for(int i = Tot; i >= 1; --i)
{
if(!Ch[0][i])
{
Dp1[0][i] = Dp2[0][i] = 1;
Dp1[1][i] = Dp1[2][i] = Dp2[1][i] = Dp2[2][i] = 0;
continue;
}
for(int j = 0; j < 3; ++j)
{
if(Ch[1][i] == 0)
{
for(int k = 0; k < 3; ++k)
{
if(k != j)
{
Dp1[j][i] = std::max(Dp1[j][i], Dp1[k][Ch[0][i]]);
Dp2[j][i] = std::min(Dp2[j][i], Dp2[k][Ch[0][i]]);
}
}
}
else
{
for(int k = 0; k < 3; ++k)
{
for(int l = 0; l < 3; ++l)
{
if(k != l && k != j && l != j)
{
Dp1[j][i] = std::max(Dp1[j][i], Dp1[k][Ch[0][i]] + Dp1[l][Ch[1][i]]);
Dp2[j][i] = std::min(Dp2[j][i], Dp2[k][Ch[0][i]] + Dp2[l][Ch[1][i]]);
}
}
}
}
}
++Dp1[0][i];
++Dp2[0][i];
}
printf("%d %d\n", std::max(Dp1[0][1], std::max(Dp1[1][1], Dp1[2][1])), std::min(Dp2[0][1], std::min(Dp2[1][1], Dp2[2][1])));
return 0;
}