一、题目
二、解法
可以把颜色的出现情况压缩成一个二进制,然后处理出每个点到根的异或和,一条路径的异或和就是 d [ u ] ⊕ d [ v ] d[u]\oplus d[v] d[u]⊕d[v],然后就用树上启发式合并就行了,合并之前先询问,询问时可以允许至多一位上是奇数。
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 500005;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,tot,tmp,t1,f[M],ans[M],fa[M],dep[M],dis[M],siz[M],son[M],cnt[1<<22];
char s[10];
struct edge
{
int v,c,next;
}e[2*M];
void dfs1(int u,int p)
{
fa[u]=p;siz[u]=1;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v,c=e[i].c;
if(v==p) continue;
dep[v]=dep[u]+1;
dis[v]=dis[u]^(1<<c);
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
}
void cal(int u)
{
if(cnt[dis[u]]) tmp=max(tmp,cnt[dis[u]]+dep[u]-t1);
for(int i=0;i<22;i++)
{
int t=dis[u]^(1<<i);
if(cnt[t]) tmp=max(tmp,cnt[t]+dep[u]-t1);
}
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa[u]) continue;
cal(v);
}
}
void fuck(int u,int fl)
{
if(fl==1) cnt[dis[u]]=max(cnt[dis[u]],dep[u]);
else cnt[dis[u]]=0;//以前手滑了
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa[u]) continue;
fuck(v,fl);
}
}
void dfs2(int u)
{
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa[u] || v==son[u]) continue;
dfs2(v);
ans[u]=max(ans[u],ans[v]);
fuck(v,-1);
}
tmp=0;
if(son[u]) dfs2(son[u]);
ans[u]=max(ans[u],ans[son[u]]);
t1=2*dep[u];
if(cnt[dis[u]]) tmp=max(tmp,cnt[dis[u]]+dep[u]-t1);
for(int i=0;i<22;i++)
{
int t=dis[u]^(1<<i);
if(cnt[t]) tmp=max(tmp,cnt[t]+dep[u]-t1);
}
cnt[dis[u]]=max(cnt[dis[u]],dep[u]);
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa[u] || v==son[u]) continue;
cal(v);
fuck(v,1);
}
ans[u]=max(ans[u],tmp);//注意还要比较
}
int main()
{
n=read();
for(int i=2;i<=n;i++)
{
int j=read();scanf("%s",s);
e[++tot]=edge{i,s[0]-'a',f[j]},f[j]=tot;
e[++tot]=edge{j,s[0]-'a',f[i]},f[i]=tot;
}
dfs1(1,0);
dfs2(1);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
}