这是一道好题……
关于这篇文章我为什么不和(dsu) (on) (tree)放一起,因为这题太好了,单独拿出来会好些
这是可爱的链接
先大致扫一眼题意,发现一条路径满足条件,当且仅当上面至多有一种字母出现次数为奇数次,总共有(22)种,因此我们可以考虑状压,(0)表示一个字母出现偶数次,(1)表示出现奇数次,用异或处理即可。
再看一下题目,很像一道点分治的题(真的,树上路径你告诉我不像?)但是这道题是一颗定根树,树根为(1)。
显然不能点分治,但是点分治的思路可以借鉴一下,考虑树上启发式合并(dsu) (on) (tree)。
即在做答案时用点分治的方式做(一条路径可行的情况只有(23)种),维护信息时用树上启发式合并的方法取维护。
我们维护一个(f[x])表示从根节点出发路径异或之后的值为(x)的最大深度。
这一题因为用到了点分治的思路,所以相当于我们做答案时是先算了(x)的每个子树的答案,在考虑所有过(x)的路径,因此我们在(dsu) (on) (tree)上做答案时递归需要保留(x)(但这种情况我还没改出来,有知道的怎么改的跟我讲一下好吗?)
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int f=1,w=0;char x=0;
while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
return w*f;
}
const int N=500010;
int num_edge,n,Tim,Id[N];
int head[N],Dep[N],Xor[N],f[1<<22],End[N];
int Vis[N],Siz[N],Max[N],ans[N],Dfn[N];
struct Edge{int next,to,dis;} edge[N];
inline void Add(int from,int to,int dis)
{
edge[++num_edge].next=head[from];
edge[num_edge].dis=dis;
edge[num_edge].to=to;
head[from]=num_edge;
}
inline void Dfs_For_Pre(int pos,int fth,int dis)
{
Dfn[pos]=++Tim,Id[Dfn[pos]]=pos;Siz[pos]=1;
Dep[pos]=Dep[fth]+1;Xor[pos]=Xor[fth]^dis;
for(int i=head[pos];i;i=edge[i].next)
{
Dfs_For_Pre(edge[i].to,pos,edge[i].dis);
Siz[pos]+=Siz[edge[i].to];
if(Siz[Max[pos]]<Siz[edge[i].to]) Max[pos]=edge[i].to;
}
End[pos]=Tim;
}
/*inline void Update(int pos,int Anc)
{
if(f[Xor[pos]]) ans[Anc]=max(ans[Anc],f[Xor[pos]]+Dep[pos]-(Dep[Anc]<<1));
for(int j=0;j<22;j++)
if(f[Xor[pos]^(1<<j)])
ans[Anc]=max(ans[Anc],f[Xor[pos]^(1<<j)]+Dep[pos]-(Dep[Anc]<<1));
for(int i=head[pos];i;i=edge[i].next)
if(!Vis[edge[i].to]) Update(edge[i].to,Anc);
}
inline void Cancel(int pos)
{
for(int i=head[pos];i;i=edge[i].next)
f[Xor[edge[i].to]]=0,Cancel(edge[i].to);
}
inline void Change(int pos)
{
for(int i=head[pos];i;i=edge[i].next)
f[Xor[edge[i].to]]=max(f[Xor[edge[i].to]],Dep[edge[i].to]),Change(edge[i].to);
}
inline void Dsu_On_Tree(int pos,int Jud)
{
for(int i=head[pos];i;i=edge[i].next)
if(edge[i].to!=Max[pos])
Dsu_On_Tree(edge[i].to,0),ans[pos]=max(ans[pos],ans[edge[i].to]);
if(Max[pos]) Dsu_On_Tree(Max[pos],1),ans[pos]=max(ans[pos],ans[Max[pos]]),Vis[Max[pos]]=1;
if(f[Xor[pos]]) ans[pos]=max(ans[pos],f[Xor[pos]]-Dep[pos]);
for(int i=0;i<22;i++)
if(f[Xor[pos]^(1<<i)]) ans[pos]=max(ans[pos],f[Xor[pos]^(1<<i)]-Dep[pos]);
f[Xor[pos]]=max(f[Xor[pos]],Dep[pos]);
for(int i=head[pos];i;i=edge[i].next)
if(!Vis[edge[i].to]) Update(edge[i].to,pos),Change(edge[i].to);
Vis[Max[pos]]=0;if(!Jud) f[Xor[pos]]=0,Cancel(pos);
}
*/
inline void Dsu_On_Tree(int pos,int Jud)
{
for(int i=head[pos];i;i=edge[i].next)
if(edge[i].to!=Max[pos])
Dsu_On_Tree(edge[i].to,0),ans[pos]=max(ans[pos],ans[edge[i].to]);
if(Max[pos]) Dsu_On_Tree(Max[pos],1),ans[pos]=max(ans[pos],ans[Max[pos]]);
if(f[Xor[pos]]) ans[pos]=max(ans[pos],f[Xor[pos]]-Dep[pos]);
for(int i=0;i<22;i++)
if(f[Xor[pos]^(1<<i)]) ans[pos]=max(ans[pos],f[Xor[pos]^(1<<i)]-Dep[pos]);
f[Xor[pos]]=max(f[Xor[pos]],Dep[pos]);
for(int i=head[pos];i;i=edge[i].next)
if(edge[i].to!=Max[pos])
{
for(int j=Dfn[edge[i].to];j<=End[edge[i].to];j++)
{
if(f[Xor[Id[j]]]) ans[pos]=max(ans[pos],f[Xor[Id[j]]]+Dep[Id[j]]-(Dep[pos]<<1));
for(int k=0;k<22;k++)
if(f[Xor[Id[j]]^(1<<k)]) ans[pos]=max(ans[pos],f[Xor[Id[j]]^(1<<k)]+Dep[Id[j]]-(Dep[pos]<<1));
}
for(int j=Dfn[edge[i].to];j<=End[edge[i].to];j++)
f[Xor[Id[j]]]=max(f[Xor[Id[j]]],Dep[Id[j]]);
}
if(!Jud) for(int j=Dfn[pos];j<=End[pos];j++) f[Xor[Id[j]]]=0;
}
int main(){
#ifndef ONLINE_JUDGE
//freopen("A.in","r",stdin);//Ans=3 1 1 0
freopen("B.in","r",stdin);//Ans=4 1 0 1 0
#endif
char c;n=read();
for(int i=2,x;i<=n;i++)
x=read(),c=getchar(),Add(x,i,1<<(c-'a'));
Dfs_For_Pre(1,0,0);Dsu_On_Tree(1,0);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}