[CF741D]Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths Description sol code

luogu

一棵以(1)号点为根的有根树,每条边上有一个小写字母(a~v)。定义一条路经是好的,当且仅当这条路径上经过的所有小写字母重排后可以构成回文串。
求以每个点为根的子树中最长的好的路径。

sol

算法发明者自己出的题,可以去他的blog里面看看。

首先,对于一条字母是(ch)的边,定义其权值为(2^{ch-'a'})
这样一条路经是好的就当且仅当这条路径的异或和二进制位中的(1)的个数不超过(1)
在处理以某一点为根的子树时,开桶,记(f[i])表示到根路径异或和为(i)的点的最大深度,可以类似点分的方法计算答案并更新桶。
现在问题的瓶颈在于:每棵子树需要分别处理,把结果全部保存下来空间开不下,而每次重新计算一遍时间上吃不消。
考虑(dsu on tree)
先递归处理轻儿子,删除轻儿子的贡献,递归处理重儿子,保存其贡献,再把轻儿子全部再算一遍。
复杂度是(O(nlog n))的。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 5e5+5;
int n,nxt[N],val[N],head[N],dfn[N],low[N],tim,id[N],dep[N],sz[N],son[N],Xor[N],f[1<<22],ans[N];
void dfs1(int u){
	id[dfn[u]=++tim]=u;sz[u]=1;
	for (int v=head[u];v;v=nxt[v]){
		dep[v]=dep[u]+1;Xor[v]=Xor[u]^val[v];
		dfs1(v);sz[u]+=sz[v];
		if (sz[v]>sz[son[u]]) son[u]=v;
	}
	low[u]=tim;
}
void dfs(int u,int keep){
	for (int v=head[u];v;v=nxt[v])
		if (v!=son[u]) dfs(v,0),ans[u]=max(ans[u],ans[v]);
	if (son[u]) dfs(son[u],1),ans[u]=max(ans[u],ans[son[u]]);

	if (f[Xor[u]]) ans[u]=max(ans[u],f[Xor[u]]-dep[u]);
	for (int i=0;i<22;++i)
		if (f[Xor[u]^(1<<i)]) ans[u]=max(ans[u],f[Xor[u]^(1<<i)]-dep[u]);
	f[Xor[u]]=max(f[Xor[u]],dep[u]);

	for (int v=head[u];v;v=nxt[v])
		if (v!=son[u]){
			for (int i=dfn[v];i<=low[v];++i){
				if (f[Xor[id[i]]]) ans[u]=max(ans[u],f[Xor[id[i]]]+dep[id[i]]-(dep[u]<<1));
				for (int j=0;j<22;++j)
					if (f[Xor[id[i]]^(1<<j)])
						ans[u]=max(ans[u],f[Xor[id[i]]^(1<<j)]+dep[id[i]]-(dep[u]<<1));
			}
			for (int i=dfn[v];i<=low[v];++i){
				f[Xor[id[i]]]=max(f[Xor[id[i]]],dep[id[i]]);
			}
		}
	if (!keep) for (int i=dfn[u];i<=low[u];++i) f[Xor[id[i]]]=0;
}
int main(){
	n=gi();
	for (int i=2;i<=n;++i){
		int f=gi();char ch=getchar();
		nxt[i]=head[f];val[i]=1<<ch-'a';head[f]=i;
	}
	dep[1]=1;dfs1(1);dfs(1,0);
	for (int i=1;i<=n;++i) printf("%d ",ans[i]);
	puts("");return 0;
}