题意:给定一棵 点有根树,每条树边上有 到 里的某个字母。
求每个子树中最长,满足路径上的字母重排之后可以构成回文串的路径。
CF741是单片高性能内补偿运算放大器
显然回文串里面最多只有一个字母出现奇数次,可以用 位的二进制数来表示每个字母出现次数的奇偶情况。
记 为从总根 到 路径上每个字母出现次数的奇偶情况(压进二进制数里)。
考虑现在正在求以 为根的子树的答案 。
数据范围明示复杂度
很明显这不是一个点分治题目,不过这个既视感还是挺重的
考虑把路径分为过 和不过 的
不经过 的交给下面的 去搞,过 的就地解决
过 的也可以分成两种(虽然不分也行),一种是有一个端点是根的,
另外那种,枚举到某个点 的时候找出满足 的 最大的
只需要枚举 一共 种异或结果然后拿 去对出 就行了
对于每个 记录一个
那么可以树剖暴力或者搞树上启发式合并
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cctype>
using namespace std;
#define add_edge(u,v,w) nxt[++tot]=head[u],to[tot]=v,head[u]=tot,val[tot]=1<<w
int N,t,tot=0,mx=0,mn;
int head[500005]={},nxt[500005]={},to[500005]={},val[500005]={}; //链式前向星
int dep[500005]={},sum[500005]={},mxdep[5000005]={}; //深度,异或和,异或和最大深度
int siz[500005]={},son[500005]={},ans[500005]={}; //子树大小,重儿子,答案
char ch;
void pthDec(int x) //树链剖分
{
siz[x]=1; //初始化子树大小为1
for(int i=head[x];i;i=nxt[i])
{
dep[to[i]]=dep[x]+1; sum[to[i]]=sum[x]^val[i]; //更新子节点的深度,到根路径的权值异或和
pthDec(to[i]); //递归
siz[x]+=siz[to[i]]; //更新子树大小
if(siz[to[i]]>siz[son[x]])son[x]=to[i]; //维护重儿子
}
}
void calc(int x,int root) //计算答案
{
mx=max(mx,mxdep[sum[x]]+dep[x]-2*dep[root]); //不到根,xorsum=0
for(int i=0;i<22;++i)mx=max(mx,mxdep[(1<<i)^sum[x]]+dep[x]-2*dep[root]); //不到根,xorsum=2^p
//这两句决定了mxdep要初始化为-inf
if(!(sum[x]^sum[root]))mx=max(mx,dep[x]-dep[root]); //到根,xorsum=0
for(int i=0;i<22;++i)if((sum[x]^sum[root])==(1<<i))mx=max(mx,dep[x]-dep[root]); //到根,xorsum=2^p
for(int i=head[x];i;i=nxt[i])calc(to[i],root); //递归
}
void modify(int x,bool type) //统计或者抹除贡献
{
mxdep[sum[x]]=type?max(mxdep[sum[x]],dep[x]):mn; //更新mxdep
for(int i=head[x];i;i=nxt[i])modify(to[i],type); //递归
}
void dfs(int x,bool keep)
{
for(int i=head[x];i;i=nxt[i])
{
if(to[i]==son[x])continue;
dfs(to[i],0); //递归轻儿子
}
if(son[x])dfs(son[x],1); //递归重儿子
mx=max(0,mxdep[sum[x]]-dep[x]); //重儿子,xorsum=0
for(int i=0;i<22;++i)mx=max(mx,mxdep[(1<<i)^sum[x]]-dep[x]); //重儿子,xorsum=2^p
for(int i=head[x];i;i=nxt[i])
{
if(to[i]==son[x])continue;
calc(to[i],x); modify(to[i],1); //暴力统计轻儿子贡献
}
ans[x]=mx; //保存答案
if(keep)mxdep[sum[x]]=max(mxdep[sum[x]],dep[x]); //保存贡献
else //抹除贡献
{
for(int i=head[x];i;i=nxt[i])modify(to[i],0); //子节点的
mxdep[sum[x]]=0; //根的
}
}
void sumans(int x) //树形dp合并答案
{
for(int i=head[x];i;i=nxt[i])
{
sumans(to[i]); //递归
ans[x]=max(ans[x],ans[to[i]]); //合并
}
}
int main()
{
memset(mxdep,128,sizeof(mxdep)); mn=mxdep[0]; //初始化为极小值-inf
scanf("%d",&N);
for(int i=2;i<=N;++i)
{
scanf("%d %c ",&t,&ch);
add_edge(t,i,(int)(ch-'a'));
} //读入,连边
pthDec(1); //树剖
dfs(1,0); //暴力统计每个点独立的答案(过每个点的路径)
sumans(1); //合并统计答案(把子树的答案合并到根)
for(int i=1;i<=N;++i)printf("%d ",ans[i]); //输出
return 0;
}