每条边是一个字母,且最多22种,我们可以转化成一个二进制数,那么一条简单路径如果能重排得到回文串就等价为边权异或结果为2的幂数。(二进制下最多含有1个1)
带边权路径问题,我们除了点分治之外可以枚举LCA,然后在LCA的子树中求解,再利用dsu on tree优化时间复杂度。
最终把问题转化成:dis[i]表示根节点(1)到节点i的路径上边的异或结果,dep[i]表示i的深度,求满足dis[i] XOR dis[j] ==(2的幂数)
的情况下dep[i]+dep[j]-2*dep[LCA]
的最大值。 而2的幂数,在本题中一共只有23种,枚举一遍即可。
枚举子树根节点作为LCA,dsu on tree。处理完一棵子树的答案再更新信息。
如何处理某棵子树的答案呢?
dp[i]表示dis[u]==i的节点中,最大的深度。维护它就行。
以rt为根的子树,答案来源有三种:
①从rt出发到达孩子子树中的路径
②经过了rt,存在于两棵孩子子树的路径
③与rt无关,单独存在于某棵孩子子树的答案
三者务必枚举完全并取max。
时间复杂度23*O(nlogn)
PS:因为没有事先想清楚答案的来源,导致漏了第三种情况,wa了一晚上…
属实是学傻了捏。
以后遇到这种涉及LCA,复杂一点的,一定要先想明白再开始写。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 5e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e17+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';ch=getchar();}
return x*f;
}
//有根树 每条边有一个字符 'a'~'v' 每颗子树询问 这颗子树中 最长的简单路径 经过的字符重排可得回文串
//字符压缩成2进制串 异或结果为2的幂 或者 0
//dis[u]^dis[v]=2^x/0 dep[u]+dep[v]-2dep[lca]最大
int n;
int max_dep[1<<22];//异或结果为i的 最大的dep dis数组的上界是(1<<22 -1)
int dep[maxn],dis[maxn];
vector<pii> g[maxn];
int in[maxn],clk,sz[maxn],son[maxn],fa[maxn],pos[maxn];
int ans[maxn];//每个点的答案 与rt无关纯子树中 由rt出发的路径 经过rt的路径
void dfs(int rt)
{
sz[rt]=1;
in[rt]=++clk;pos[clk]=rt;
dep[rt]=dep[fa[rt]]+1;
for(pii i:g[rt])
{
dis[i.first]=dis[rt]^i.second;
dfs(i.first);
sz[rt]+=sz[i.first];
if(sz[i.first] > sz[son[rt]]) son[rt]=i.first;
}
}
int lca;
inline void solve(int u)//统计这个点的贡献
{
//0
int sta=dis[u];
if(max_dep[sta])
ans[lca]=max(ans[lca],dep[u]+max_dep[sta]-2*dep[lca]);
for(int i=1;i<=22;i++)
{
int sta=dis[u]^(1<<(i-1));
if(max_dep[sta])
ans[lca]=max(ans[lca],dep[u]+max_dep[sta]-2*dep[lca]);
}
}
inline void upd(int u)//加入u 维护max_dep
{
max_dep[dis[u]]=max(max_dep[dis[u]],dep[u]);
}
inline void add(int rt)//先统计 再维护
{
for(int i=in[rt];i<in[rt]+sz[rt];i++)
{
int u=pos[i];
solve(u);
}
for(int i=in[rt];i<in[rt]+sz[rt];i++)
{
int u=pos[i];
upd(u);
}
}
void dfs1(int rt,bool save)
{
for(pii i:g[rt])
{
if(i.first==son[rt]) continue;
dfs1(i.first,0);
ans[rt]=max(ans[rt],ans[i.first]);
}
if(son[rt]) dfs1(son[rt],1),ans[rt]=max(ans[rt],ans[son[rt]]);
lca=rt;
//重儿子保留信息
solve(rt);//rt->重儿子的路径
upd(rt);
for(pii i:g[rt])
{
if(i.first==son[rt]) continue;
add(i.first);
}
if(!save) //全部清空
{
for(int i=in[rt];i<in[rt]+sz[rt];i++)
{
int u=pos[i];
max_dep[dis[u]]=0;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
char ch;
scanf("%d %c",&fa[i],&ch);
g[fa[i]].push_back({
i,1<<(ch-'a')});
}
dfs(1);
dfs1(1,1);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}