題意:給定一棵有根樹,結點編號爲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;
}