CF741D-Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths【树上启发式合并】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/85171470

正题

评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=CF741D


题目大意

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


解题思路

对于一堆重新排列后可以变成回文串的字母,仅当只有少于2个字母出现了奇数次。所以就只需要记录奇偶问题,我们可以状压一下。
求出从根到每个点的值,之后x到y的路径就可以表示为 w x   x o r   w y w_x\ xor\ w_y ,因为 L C A LCA 之上的都会相互抵消。

我们得到 O ( n 2   l o g   n ) O(n^2\ log\ n) 的做法。之后用树上启发式合并就可以变为 O ( n   l o g 2   n ) O(n\ log^2\ n)


c o d e code

#include<cstdio>
#include<algorithm>
#define N 500010
using namespace std;
struct node{
	int to,next,w;
}a[N];
int tot,ls[N],size[N],dep[N],val[N];
int len[1<<22],ans[N],dfn[N],rfn[N],ed[N];
int cnt,n,son[N],dpt[N];
void addl(int x,int y,int w)//加边
{
	a[++tot].to=y;
	a[tot].next=ls[x];
	a[tot].w=w;
	ls[x]=tot;
}
void dfs(int x)//第一次搜索需要信息
{
	size[x]++;
	for(int i=ls[x];i;i=a[i].next)
	{
		int y=a[i].to;
		dep[y]=dep[x]+1;
		val[y]=val[x]^(1<<a[i].w);
		dfs(y);
		if(size[y]>size[son[x]])
		  son[x]=y;
		size[x]+=size[y];
	}
}
int get_ans(int x)//计算从x出发的最长路径长度
{
	int ans=0;
	if(len[val[x]])
	  ans=dep[x]+len[val[x]];
	for(int i=0;i<22;i++)
	  if(len[val[x]^(1<<i)])
	      ans=max(ans,dep[x]+len[val[x]^(1<<i)]);
	return ans;
}
void dus(int x,int top)//树上启发式合并
{
	dfn[++cnt]=x;
	rfn[x]=cnt;
	for(int i=ls[x];i;i=a[i].next)//搜索除了最大的子树
	  if(a[i].to!=son[x])
	  {
	  	  dus(a[i].to,a[i].to);
	  	  ans[x]=max(ans[x],ans[a[i].to]);
	  }
	int mid=cnt;
	if(son[x])//搜索最大的子数
	{
		dus(son[x],top);
		ans[x]=max(ans[x],ans[son[x]]);
	}
	ed[x]=cnt;
	for(int i=rfn[x]+1;i<=mid;i=ed[dfn[i]]+1)//枚举子树
	{
		for(int j=i;j<=ed[dfn[i]];j++)//扫描这个子树的答案
		  ans[x]=max(ans[x],get_ans(dfn[j])-2*dep[x]);
		for(int j=i;j<=ed[dfn[i]];j++)//将这个子树加入可扫描答案
		  len[val[dfn[j]]]=max(len[val[dfn[j]]],dep[dfn[j]]);
		//分两次for不会使得起点和终点在同一棵子树
	}
	ans[x]=max(ans[x],get_ans(x)-2*dep[x]);//自己为起点
	len[val[x]]=max(len[val[x]],dep[x]);//更新
	if(x==top)//不保留该子树信息
	  for(int i=rfn[x];i<=ed[x];i++)
	    len[val[dfn[i]]]=0;
}
int main()
{
	scanf("%d",&n);
	for(int i=2;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		char c=getchar(); 
		while (c<'a'||c>'v') 
		  c=getchar();
		addl(x,i,c-'a');
	}
	dep[0]=-1;
	dfs(1);
	dus(1,1);
	for(int i=1;i<=n;i++)
	  printf("%d ",ans[i]);
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/85171470