题意:给定一棵有根树,结点编号为1~n,根结点为结点1。每条边上有一个字母,求各子树内最长的满足“路径上的字母经过重排后可以构成回文串”的简单路径。
分析:对结点u,用2进制数vec[u]表示结点u到根结点的路径上各字母的数目的奇偶性。若第i个字母数目为奇数则vec[u]的第i位为1,否则为0。子树u内满足要求的路径可以分成两类,一类经过结点u,一类不经过结点u。由于不经过结点u的路径的长度最大值可以递归求得,因此这里仅需考虑经过结点u的路径。用cnt[S]表示子树u内vec[]值为S的结点的最大深度,那么对子树u内两点v1,v2,当v1与v2不在结点u的同一个儿子子树内且vec[v1]^vec[v2]的二进制表示的1的数量不超过1时,v1到v2的路径即为一条经过结点u的路径。于是我们可以在线性时间内完成对子树u经过结点u的满足要求的路径的最大长度值的计算。再用启发式合并就可以把这个暴力过程优化到O(22nlgn)。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10,maxl=22,maxS=1<<22;
struct edge
{
int u,v,d;
};
int n,ans[maxn];
int sz[maxn],t_c,st[maxn],ft[maxn],ver[maxn],vec[maxn],h[maxn],cnt[maxS];
vector<edge> e;
vector<int> G[maxn];
int idx(char c)
{
return c-'a';
}
void add(int u,int v,int d)
{
e.push_back((edge){u,v,d});
int k=e.size();
G[u].push_back(k-1);
}
void dfs1(int u,int fu,int h1)
{
h[u]=h1;
sz[u]=1;
st[u]=++t_c;
ver[t_c]=u;
for (int i=0;i<G[u].size();i++)
{
edge e1=e[G[u][i]];
int v=e1.v;
if (v==fu) continue;
vec[v]=vec[u]^(1<<e1.d);
dfs1(v,u,h1+1);
sz[u]+=sz[v];
}
ft[u]=t_c;
}
void dfs2(int u,int fu,bool keep)
{
int bc=-1;
for (int i=0;i<G[u].size();i++)
{
edge e1=e[G[u][i]];
int v=e1.v;
if (v==fu) continue;
if (bc==-1||sz[bc]<sz[v]) bc=v;
}
for (int i=0;i<G[u].size();i++)
{
edge e1=e[G[u][i]];
int v=e1.v;
if (v==fu||v==bc) continue;
dfs2(v,u,0);
}
if (bc!=-1) dfs2(bc,u,1),ans[u]=ans[bc];
for (int i=0;i<G[u].size();i++)
{
edge e1=e[G[u][i]];
int v=e1.v;
if (v==fu||v==bc) continue;
ans[u]=max(ans[u],ans[v]);
for (int j=st[v];j<=ft[v];j++)
{
int w=ver[j];
if (cnt[vec[w]]) ans[u]=max(ans[u],cnt[vec[w]]-h[u]+h[w]-h[u]);
for (int k=0;k<maxl;k++)
{
int S=(vec[w]^(1<<k));
if (cnt[S]) ans[u]=max(ans[u],cnt[S]-h[u]+h[w]-h[u]);
}
}
for (int j=st[v];j<=ft[v];j++)
{
int w=ver[j];
cnt[vec[w]]=max(cnt[vec[w]],h[w]);
}
}
if (cnt[vec[u]]) ans[u]=max(ans[u],cnt[vec[u]]-h[u]);
for (int k=0;k<maxl;k++)
{
int S=(vec[u]^(1<<k));
if (cnt[S]) ans[u]=max(ans[u],cnt[S]-h[u]);
}
cnt[vec[u]]=max(cnt[vec[u]],h[u]);
if (!keep)
for (int j=st[u];j<=ft[u];j++)
{
int w=ver[j];
cnt[vec[w]]=0;
}
}
int main()
{
cin>>n;
for (int i=1;i<n;i++)
{
int u,d;
char ch[10];
scanf("%d%s",&u,ch);
d=idx(ch[0]);
add(u,i+1,d);add(i+1,u,d);
}
dfs1(1,0,0);
dfs2(1,0,0);
for (int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}