rt,jr的思路是 为了正常使用manacher的最长回文半径数组,把空的结点存起来,防止因为位置原因出错,但一直只有64pts,后面的点全部挂了,并与答案差的非常非常远(答案个位数,我的输出五位数)数组空间开够了,一直找不到bug
代码有比较详细的注释,求调/给出hack数据/指出问题所在
# include <cstdio>
# include <cstring>
# define N 1e5+5
using namespace std;
int n,l[1000010 * 5],r[1000010 * 5],ize[1000010 * 2],a[1000010 * 5],c[1000010 * 2],an,id,xr,hp,rel[1000010 * 2]; //因为存空点 存树上的点的都开了两倍 又因为manacher 和 manacher有关的又再存了两倍
//有些数组反复使用
//后面的真长指的是不含空点的,用来当作答案
//后面的假长是含空点的,用来判定是否符合要求
void dfs(int p)
{
if (!p || (!l[p] && !r[p])) // 如果我为空或我是叶结点
{
ize[p] = 1; //假长都为1
if (p) rel[p] = 1; //真长只记真点
c[++hp] = p;//p进队
return ;
}
dfs(l[p]);//找左边
c[++hp] = p;//记录根节点
dfs(r[p]);//找右边
ize[p] = 1;
rel[p] = 1;
ize[p] = ize[p] + ize[l[p]] + ize[r[p]]; //更新我的假长
rel[p] = rel[p] + rel[l[p]] + rel[r[p]]; //更新我的真长
}
int main()
{
freopen("1.in","r",stdin);
scanf("%d",&n);
for (int i = 1; i <= n; i++) scanf("%d",&a[i]);
for (int i = 1; i <= n; i++) scanf("%d %d",&l[i],&r[i]),l[i] = l[i] > 0 ? l[i] : 0,r[i] = r[i] > 0 ? r[i] : 0; //把空点预处理为0
dfs(1);
n = 0; //回收
r[0] = -2; //manacher传统艺能
for (int i = 1; i <= hp; i++) r[++n] = -1,r[++n] = a[c[i]],l[n] = c[i]; //同艺能 此时r数组用来存要处理的串 l数组用来存 在该串第i个位置 对应的是树中哪个节点
r[++n] = -1;//补一个井号
r[++n] = -3;//防止出错 因为有空点
memset(a,0,sizeof(a)); //回收利用a数组 它是回文半径数组
for (int i = 1; i <= n; i++)
{
// xr是最靠右的回文串 id是它的中点
if (a[i] <= xr) a[i] = a[id * 2 - i] < xr - i + 1 ? a[id * 2 - i] : xr - i + 1;
while(r[i - a[i]] == r[i + a[i]]) a[i]++; //传统艺能之暴力拓展
if (i + a[i] - 1 > xr) id = i,xr = i + a[i] - 1; // 如果这个串更靠右 更新
// 回文实际长度为 a[i] - 1
if (r[i] > 0 && l[i] > 0 && (a[i] - 1) >= ize[l[i]]) //如果回文长度大过子树长度(含空点)
{
//if (rel[an] < rel[l[i]]) printf("{%d %d %d %d %d %d} \n",an,l[i],rel[l[i]],rel[an],ize[l[i]],a[i]);
an = rel[an] >= rel[l[i]] ? an : l[i]; //如果比答案更大
}
}
// for (int i = 0; i <= n; i++) printf("%d ",r[i]);
// printf("\n");
// for (int i = 1; i <= n; i++) printf("{%d %d %d %d %d}\n",i,r[i],a[i],ize[l[i]],l[i]);
// printf("\n");
printf("%d",rel[an]);
return 0;
}