【题解】codeforces741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

题目链接

题意:给定一棵有根树,结点编号为1~n,根结点为结点1。每条边上有一个字母,求各子树内最长的满足“路径上的字母经过重排后可以构成回文串”的简单路径。

分析:对结点u,用2进制数vec[u]表示结点u到根结点的路径上各字母的数目的奇偶性。若第i个字母数目为奇数则vec[u]的第i位为1,否则为0。子树u内满足要求的路径可以分成两类,一类经过结点u,一类不经过结点u。由于不经过结点u的路径的长度最大值可以递归求得,因此这里仅需考虑经过结点u的路径。用cnt[S]表示子树u内vec[]值为S的结点的最大深度,那么对子树u内两点v1,v2,当v1与v2不在结点u的同一个儿子子树内且vec[v1]^vec[v2]的二进制表示的1的数量不超过1时,v1到v2的路径即为一条经过结点u的路径。于是我们可以在线性时间内完成对子树u经过结点u的满足要求的路径的最大长度值的计算。再用启发式合并就可以把这个暴力过程优化到O(22nlgn)。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10,maxl=22,maxS=1<<22;
struct edge
{
	int u,v,d;
};
int n,ans[maxn];
int sz[maxn],t_c,st[maxn],ft[maxn],ver[maxn],vec[maxn],h[maxn],cnt[maxS];
vector<edge> e;
vector<int> G[maxn];
int idx(char c)
{
	return c-'a';
}
void add(int u,int v,int d)
{
	e.push_back((edge){u,v,d});
	int k=e.size();
	G[u].push_back(k-1);
}
void dfs1(int u,int fu,int h1)
{
	h[u]=h1;
	sz[u]=1;
	st[u]=++t_c;
	ver[t_c]=u;
	for (int i=0;i<G[u].size();i++)
	{
		edge e1=e[G[u][i]];
		int v=e1.v;
		if (v==fu) continue;
		vec[v]=vec[u]^(1<<e1.d);
		dfs1(v,u,h1+1);
		sz[u]+=sz[v];
	}
	ft[u]=t_c;
}
void dfs2(int u,int fu,bool keep)
{
	int bc=-1;
	for (int i=0;i<G[u].size();i++)
	{
		edge e1=e[G[u][i]];
		int v=e1.v;
		if (v==fu) continue;
		if (bc==-1||sz[bc]<sz[v]) bc=v;
	}
	for (int i=0;i<G[u].size();i++)
	{
		edge e1=e[G[u][i]];
		int v=e1.v;
		if (v==fu||v==bc) continue;
		dfs2(v,u,0);
	}
	if (bc!=-1) dfs2(bc,u,1),ans[u]=ans[bc];
	for (int i=0;i<G[u].size();i++)
	{
		edge e1=e[G[u][i]];
		int v=e1.v;
		if (v==fu||v==bc) continue;
		ans[u]=max(ans[u],ans[v]);
		for (int j=st[v];j<=ft[v];j++)
	    {
	    	int w=ver[j];
	    	if (cnt[vec[w]]) ans[u]=max(ans[u],cnt[vec[w]]-h[u]+h[w]-h[u]);
	    	for (int k=0;k<maxl;k++)
	    	{
	    		int S=(vec[w]^(1<<k));
	    		if (cnt[S]) ans[u]=max(ans[u],cnt[S]-h[u]+h[w]-h[u]);
			}
		}
		for (int j=st[v];j<=ft[v];j++)
		{
			int w=ver[j];
			cnt[vec[w]]=max(cnt[vec[w]],h[w]);
		}
	}
	if (cnt[vec[u]]) ans[u]=max(ans[u],cnt[vec[u]]-h[u]);
	for (int k=0;k<maxl;k++)
	{
	    int S=(vec[u]^(1<<k));
	    if (cnt[S]) ans[u]=max(ans[u],cnt[S]-h[u]);
	}
	cnt[vec[u]]=max(cnt[vec[u]],h[u]);
	if (!keep)
		for (int j=st[u];j<=ft[u];j++)
		{
			int w=ver[j];
			cnt[vec[w]]=0;
		}
}
int main()
{
	cin>>n;
	for (int i=1;i<n;i++)
	{
		int u,d;
		char ch[10];
		scanf("%d%s",&u,ch);
		d=idx(ch[0]);
		add(u,i+1,d);add(i+1,u,d);
	}
	dfs1(1,0,0);
	dfs2(1,0,0);
	for (int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值