[CF741D]Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(DSU on tree)

题面

https://codeforces.com/contest/741/problem/D

题解

前置知识:

首先,对题目中”一条路径上的字符,重排后能够构成回文串“这个条件做一下转化。

  • 如果路径长是偶数,那么等价于:在这条路径上,所有字符出现的个数都是偶数。
  • 如果路径长是奇数,那么等价于:在这条路径上,有且仅有一种字符出现的个数是奇数。

因此,我们可以对于每一个树上节点u,维护从u到根的路径上,各字符出现的个数的奇偶性。存储可以二进制压位。设这个值为\(mask[u]\)

那么一条路径\(path(u,v)\)是合法的,当且仅当\(pop{\_}cnt(mask[u] \bigoplus mask[v]) \leq 1\)\(pop{\_}cnt[u]\)表示u在二进制表示下的1的个数)。它只有23种选择。

考虑统计经过某一个点u,所有合法串的最大值ans[u]。可以使用DSU on tree来做。

具体地说,维护f[x]表示当前所有mask[u]=x的u的深度最大值。在dfs到u点时,f中存储的将是u重儿子的信息。然后,逐一遍历u每一个轻儿子v,对于v子树中的所有点更新ans[u],再更新f。再对点u本身更新ans[u]和f。之后如果u不是fa[u]的重儿子则清空f。整个处理方法类似于点分治。

过程中,“对于v更新ans[u]”指的是,需要找到能够与v形成合法路径的w的深度的最大值。而我们知道了mask[v],就确定了mask[w]的全部23种可能性,只需去f中查找,并取最大值得到cur。然后使ans[u]与\(cur+dep[v]-2 \times dep[u]\)取较大值。

总时间复杂度\(O(|C|n \log n)\)。其中\(|C|\)为字符集大小,本题中为22。

代码

#include<bits/stdc++.h>

using namespace std;

#define rg register
#define In inline

const int N = 5e5;
const int M = 1 << 22;
const int inf = 0x3f3f3f3f;

namespace IO{
	In int read(){
		int s = 0,ww = 1;
		char ch = getchar();
		while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
		while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
		return s * ww;
	}
	In void write(int x){
		if(x < 0)putchar('-'),x = -x;
		if(x > 9)write(x / 10);
		putchar('0' + x % 10);
	}
}
using namespace IO;

int f[M+5],ans[N+5];
int mask[N+5];

struct edge{
	int next,des,ch;
}e[N+5];

int cnt;
int head[N+5];

In void addedge(int a,int b,char ch){
	cnt++;
	e[cnt].des = b;
	e[cnt].next = head[a];
	e[cnt].ch = ch - 'a';
	head[a] = cnt; 
}

int son[N+5],sz[N+5],dfn[N+5],D[N+5],dep[N+5];
int dn;

void prepro(int u,int msk){
	mask[u] = msk;
	dn++;
	D[dn] = u,dfn[u] = dn;
	sz[u] = 1;
	int maxn = 0;
	for(rg int i = head[u];i;i = e[i].next){
		int v = e[i].des;
		dep[v] = dep[u] + 1;
		prepro(v,msk ^ (1<<e[i].ch));
		sz[u] += sz[v];
		if(sz[v] > maxn)maxn = sz[v],son[u] = v;
	}
}		

In int query(int cur){
	int rt = f[cur];
	for(rg int i = 0;i < 22;i++)rt = max(rt,f[cur^(1<<i)]);	
	return rt;
}

void dfs(int u,int keep){
	for(rg int i = head[u];i;i = e[i].next){
		int v = e[i].des;
		if(v == son[u])continue;
		dfs(v,0);
	}
	if(son[u])dfs(son[u],1);
	for(rg int i = head[u];i;i = e[i].next){
		int v = e[i].des;
		if(v == son[u])continue;
		for(rg int j = dfn[v];j <= dfn[v] + sz[v] - 1;j++){
			int cur = mask[D[j]];
			ans[u] = max(ans[u],query(cur) + dep[D[j]] - 2 * dep[u]);
		}
		for(rg int j = dfn[v];j < dfn[v] + sz[v];j++)f[mask[D[j]]] = max(f[mask[D[j]]],dep[D[j]]);
	}
	ans[u] = max(ans[u],query(mask[u]) - dep[u]);
	f[mask[u]] = max(f[mask[u]],dep[u]);
	for(rg int i = head[u];i;i = e[i].next)ans[u] = max(ans[u],ans[e[i].des]);
	if(!keep){
		for(rg int i = dfn[u];i <= dfn[u] + sz[u] - 1;i++)
			f[mask[D[i]]] = -inf;
	}
}

int main(){
//	freopen("CF741D.in","r",stdin);
//	freopen("CF741D.out","w",stdout);
	int n = read();
	for(rg int i = 0;i < (1 << 22);i++)f[i] = -inf;
	for(rg int i = 2;i <= n;i++){
		int f = read();
		char ch = getchar();
		addedge(f,i,ch);
	}
	prepro(1,0);
	dfs(1,1);
	for(rg int i = 1;i <= n;i++){
		write(ans[i]);
		putchar(i == n ? '\n' : ' ');
	}
	return 0;
}

posted @ 2020-10-06 19:49  coder66  阅读(149)  评论(0编辑  收藏  举报