题目
题目链接:https://codeforces.ml/problemset/problem/741/D
一棵根为 \(1\) 的树,每条边上有一个字符( \(a\sim v\) 共 \(22\) 种)。一条简单路径被称为 Dokhtar-kosh 当且仅当路径上的字符经过重新排序后可以变成一个回文串。 求每个子树中最长的 Dokhtar-kosh 路径的长度。
思路
明显地要把一条路径之间所有字母状压,然后一条路径是 Dk 路径当且仅当状压后最多包含一个 \(1\)。
设 \(a[x]\) 表示点 \(x\) 到根的路径中字母的状态,那么对于一条 \(u\) 到 \(v\) 的路径,其状态就是 \(a[u]\operatorname{xor} a[v]\)。
记 \(maxd[s]\) 表示当前搜索到的子树中状态为 \(s\) 的点最深的深度,每次计算答案的时候枚举状态为 \(1\) 的字母,乱搞即可。
直接上 dsu on tree。时间复杂度 \(O(nm\log n)\),其中 \(m\) 是字母数(即 \(22\))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=500010,M=(1<<22)+10;
int n,tot,head[N],a[N],son[N],size[N],maxd[M],dep[N],ans[N],dfn[N],id[N];
char ch[5];
struct edge
{
int next,to;
}e[N];
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void dfs1(int x,int fa)
{
dep[x]=dep[fa]+1; size[x]=1;
a[x]^=a[fa]; dfn[x]=++tot; id[tot]=x;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
dfs1(v,x);
size[x]+=size[v];
if (size[v]>size[son[x]]) son[x]=v;
}
}
void update(int &WYCTQL,int v,int x,int p)
{
if (maxd[v]) WYCTQL=max(WYCTQL,maxd[v]+dep[x]-2*dep[p]);
for (int i=0;i<22;i++)
if (maxd[v^(1<<i)]) WYCTQL=max(WYCTQL,maxd[v^(1<<i)]+dep[x]-2*dep[p]);
}
void dfs2(int x,bool clr)
{
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=son[x])
dfs2(v,1),ans[x]=max(ans[x],ans[v]);
}
if (son[x])
{
dfs2(son[x],0);
ans[x]=max(ans[x],ans[son[x]]);
}
update(ans[x],a[x],x,x);
maxd[a[x]]=max(maxd[a[x]],dep[x]);
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=son[x])
{
for (int j=dfn[v];j<dfn[v]+size[v];j++)
update(ans[x],a[id[j]],id[j],x);
for (int j=dfn[v];j<dfn[v]+size[v];j++)
maxd[a[id[j]]]=max(maxd[a[id[j]]],dep[id[j]]);
}
}
if (clr)
for (int i=dfn[x];i<dfn[x]+size[x];i++)
maxd[a[id[i]]]=0;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for (int i=2,x;i<=n;i++)
{
cin>>x>>ch;
add(x,i);
a[i]=1<<(ch[0]-'a');
}
tot=0;
dfs1(1,0); dfs2(1,0);
for (int i=1;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}