CodeForces 741D....path【树上启发式合并】

传送门

题意

一棵根为1的树,每条边上有一个字符(a-v共22种)。 一条简单路径被称为Dokhtar-kosh当且仅当路径上的字符经过重新排序后可以变成一个回文串。 求每个子树中最长的Dokhtar-kosh路径的长度。

题解

既然要重新排列,那么这个路径的条件就是:

  • 简单路径
  • 路径中 22 种字母最多只能由一种字母数量为奇数。
    如果我一直从某点到根节点的字母的情况,那么对于另一个点,如果在所有字母中奇偶性与之相同或者只有1种奇偶性不同,那么这两个点之间的简单路径就是Dokhtar-kosh。
    然后这样的统计可以使用树上启发式合并,方法和什么点分治差不多。

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <queue>
#define xx first
#define yy second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int inf=0x3f3f3f3f;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int N=5e5+10;
const int M=1e6+10;
int n,sta[N],dep[N],siz[N],son[N];
int ans[N],maxlen[1<<22],rt,node[N],cnt;
vector<PII> g[N];

void predfs(int u,int fa,int state){
	siz[u]=1;sta[u]=state;dep[u]=dep[fa]+1;
	for(PII e:g[u]){
		int v=e.xx,c=e.yy;
		if(v==fa) continue;
		predfs(v,u,state^(1<<c));
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}

void getnode(int u,int fa){
	node[++cnt]=u;
	for(PII e:g[u]) if(e.xx!=fa) getnode(e.xx,u);
}

void clear(int u,int fa){
	maxlen[sta[u]]=-inf;
	for(PII e:g[u]) if(e.xx!=fa) clear(e.xx,u);
}

void calc(int u){
	ans[rt]=max(ans[rt],maxlen[sta[u]]+dep[u]-2*dep[rt]);
	for(int i=0;i<22;i++)
		ans[rt]=max(ans[rt],maxlen[sta[u]^(1<<i)]+dep[u]-2*dep[rt]);
}

void dfs(int u,int fa,bool keep){
	for(PII e:g[u]){
		if(e.xx==fa||e.xx==son[u]) continue;
		dfs(e.xx,u,false);
	}
	if(son[u]) dfs(son[u],u,true);
	rt=u;
	calc(u);
	maxlen[sta[u]]=max(maxlen[sta[u]],dep[u]);
	for(PII e:g[u]){
		if(e.xx==fa||e.xx==son[u]) continue;
		cnt=0;
		getnode(e.xx,u);
		for(int i=1;i<=cnt;i++) calc(node[i]);
		for(int i=1;i<=cnt;i++) maxlen[sta[node[i]]]=max(maxlen[sta[node[i]]],dep[node[i]]);
	}
	for(PII e:g[u]) if(e.xx!=fa) ans[u]=max(ans[u],ans[e.xx]);
	if(!keep) clear(u,fa);
}

int main(){
	memset(maxlen,0xc0,sizeof(maxlen));
	scanf("%d",&n);
	for(int v=2,u;v<=n;v++){
		char s[10];
		scanf("%d%s",&u,s);
		g[u].push_back({v,s[0]-'a'});
		g[v].push_back({u,s[0]-'a'});
	}
	predfs(1,0,0);
	dfs(1,0,true);
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}
posted @ 2020-03-31 17:54  BakaCirno  阅读(182)  评论(0编辑  收藏  举报