Description
以(1) 为根 的 (n) 个节点的树,每条边有一个颜色 (x),求每一个点的子树内的好的路径的最长长度
一条路径被定义为好的当且仅当把所有经过的边的字母经过排列之后可以变成回文
题面
Solution
理解了一下 (dsu\,on\,tree),相比普通的启发式,省去了高级的数据结构,并省下了大量空间
好的路径实际上就是出现奇数次的字母不多于一个,字符集只有 (22),可以状压起来
对于一条路径的异或和实际上可以看成 (dis[x])^(dis[y])^(a[lca]),(dis[x]) 为点 (x) 到根的路径的异或和
那么普通的启发式合并做法就是开一个 (vector) 和 (map),分别记录每一状态下的最长链
然后从小到大合并所有的子树即可
考虑 (dsu\,on\,tree) 做法:
先按树链剖分的做法划分轻重链
然后大致思路就是:对于每一个轻儿子我们暴力合并,重儿子保留信息
为了节省空间,我们不能对每一个节点都开数组,所以开一个全局数组来维护这个东西
既然开的是全局变量,就要避免对其他子树的影响,当然就有清空操作,对于轻儿子我们暴力遍历其子树把信息删除
而一棵子树内只有一个重链,所以重链之间没有影响,可以不删除,直接保留信息
然后暴力把轻儿子子树内的信息合并,并更新答案就可以了
这样做复杂度是 (O(n*logn)) 的,因为一个点到根路径上的轻边只有 (log) 条,所以均摊复杂度是 (O(n*log)) 的
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,head[N],nxt[N],num=0,to[N],c[N],sz[N],son[N];
inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
int ans[N],dep[N],f[1<<22],o;
inline void dfs(int x){
sz[x]=1;
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
dep[u]=dep[x]+1;c[u]^=c[x];dfs(u);sz[x]+=sz[u];
if(sz[u]>sz[son[x]])son[x]=u;
}
}
inline void Delet(int x){
f[c[x]]=-N;
for(int i=head[x];i;i=nxt[i])Delet(to[i]);
}
inline void Modify(int x){
ans[o]=max(ans[o],f[c[x]]+dep[x]);
for(int i=0;i<22;i++)ans[o]=max(ans[o],f[c[x]^(1<<i)]+dep[x]);
for(int i=head[x];i;i=nxt[i])Modify(to[i]);
}
inline void ins(int x){
f[c[x]]=max(f[c[x]],dep[x]);
for(int i=head[x];i;i=nxt[i])ins(to[i]);
}
inline void dfs1(int x){
for(int i=head[x];i;i=nxt[i])
if(to[i]!=son[x])dfs1(to[i]),Delet(to[i]);
if(son[x])dfs1(son[x]);o=x;
for(int i=head[x];i;i=nxt[i])
if(to[i]!=son[x])Modify(to[i]),ins(to[i]);
f[c[x]]=max(f[c[x]],dep[x]);
ans[o]=max(ans[o],f[c[x]]+dep[x]);
for(int i=0;i<22;i++)ans[o]=max(ans[o],f[c[x]^(1<<i)]+dep[x]);
ans[o]-=dep[x]<<1;
for(int i=head[x];i;i=nxt[i])ans[x]=max(ans[x],ans[to[i]]);
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
scanf("%d",&n);
char ch[2];int x;
for(int i=(1<<22)-1;i>=0;i--)f[i]=-N;
for(int i=2;i<=n;i++){
scanf("%d%s",&x,ch);
link(x,i);
c[i]=1<<(ch[0]-'a');
}
dfs(1);dfs1(1);
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
return 0;
}