Codeforce 741 D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths (dsu on tree)

题目大意:(来源 : https://www.luogu.org/problem/CF741D)
在这里插入图片描述


若排序后能变成一个回文串,则路径上最多只有一个字符出现奇数次,用0 1 表示每个字符出现是奇还是偶。
dfs预处理每个点到根节点的所有字符出现状态 和 距离。当前点 为 i 时,最长路径要么经过 i,要么在 i 的子树中, 后者是一个子问题,可以用 dp 记录一下。

考虑经过 i 的最长路径怎么解决

一个点能和另外一个点组成 Dokhtar-kosh 路径,当且仅当两个点的状态最多只有一位不同。

由于之前预处理了每个点到根节点的状态,根据异或的性质,相同部分异或两次会抵消,i 的两个来自不同的子树的结点状态相异或得到的恰好是两点间经过 i 点的简单路径的状态。

因此可以 维护一个数组:ans[i] 表示状态到根节点的字符出现状态为 i,到根节点的最大距离。暴力搜索 i 的每一棵子树,对当前子树的每一个点,枚举可以组合的状态点更新答案,搜索完一棵子树后再将该子树的状态维护到 a n s ans ans 数组中。

复杂度: O ( 22 ∗ n log ⁡ n ) O(22 *n\log n) O(22nlogn)


#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int> 
#define fir first
#define sec second
const int maxn = 1e6 + 10;
const int inf = -0x3f3f3f3f;
int state[maxn],deep[maxn];
vector<pii> g[maxn];
int dp[maxn],n,son[maxn],f[maxn],fa[maxn];
int ans[1 << 23];
void dfs(int u) {
	son[u] = 0;f[u] = 1;
	for(auto it : g[u]) {
		deep[it.fir] = deep[u] + 1;
		state[it.fir] = state[u] ^ (1 << it.sec);
		fa[it.fir] = u;
		dfs(it.fir);
		f[u] += f[it.fir];
		if(!son[u] || f[son[u]] < f[it.fir]) son[u] = it.fir;
	}
}
void clear(int u) {
	ans[state[u]] = inf;
	for(auto it : g[u])
		clear(it.fir);
}
void upd(int u) {
	ans[state[u]] = max(ans[state[u]],deep[u]);
	for(auto it : g[u])
		upd(it.fir);
}
int qry(int u,int rt) {
	int mx = 0;
	mx = max(mx,ans[state[u]] + deep[u] - 2 * deep[rt]);
	for(int i = 0; i < 22; i++) {
		int p = state[u] ^ (1 << i);
		if(ans[p] != -1)
			mx = max(mx,ans[p] + deep[u] - 2 * deep[rt]);
	}
	for(auto it : g[u])
		mx = max(qry(it.fir,rt),mx);
	return mx;
}
void dfs2(int u,int keep) {
	dp[u] = 0;
	for(auto it : g[u])
		if(it.fir != son[u]) dfs2(it.fir,0);
		
	if(son[u]) dfs2(son[u],1);
	for(auto it : g[u]) {
		dp[u] = max(dp[u],dp[it.fir]);
		if(it.fir == son[u]) continue;
		dp[u] = max(dp[u],qry(it.fir,u));
		upd(it.fir);
	}
	dp[u] = max(dp[u],ans[state[u]] - deep[u]);
	for(int i = 0; i < 22; i++) {
		int p = state[u] ^ (1 << i);
		dp[u] = max(dp[u],ans[p] - deep[u]);
	}
	ans[state[u]] = max(ans[state[u]],deep[u]);
	if(!keep) clear(u);	
}
int main() {
	memset(ans,inf,sizeof ans);
	scanf("%d",&n);
	for(int i = 2; i <= n; i++) {
		int v;char s[2];
		scanf("%d%s",&v,s);
		int q = s[0] - 'a';
		g[v].push_back(pii(i,q));
	}
	deep[1] = 0;fa[1] = 0;
	dfs(1);dfs2(1,0);
	for(int i = 1; i <= n; i++) {
		if(i - 1) printf(" ");
		printf("%d",dp[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值