[Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths] (树上启发式合并)

话说这道题能不能用线段树合并啊,没有细想,是学习树上启发式合并时做的。

题目

洛谷

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

首先是个树上启发式合并。那么如何判回文串呢,我们只需要将字母奇偶性状态压缩进行树dp即可。还是比较板的,只是要注意一些细节。

vector<LL> G[MAXN];
LL n, dp[1 << 22], Val[MAXN], Dep[MAXN], Size[MAXN], Heavy[MAXN], Ans[MAXN];
// dp: Getting certain situation's max length

void Prepare(LL x)
{
	Size[x] = 1;
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		Val[to] = Val[x] ^ (1 << Val[to]); // Val[x] -> Root to x's Ji/Ou
		Dep[to] = Dep[x] + 1;
		Prepare( to );
		Size[x] += Size[to];
		if (! Heavy[x] || Size[Heavy[x]] < Size[to])
			Heavy[x] = to;
	}
}

LL Maxx, More;

void GetUp(LL x)
{
	dp[Val[x]] = Max(Dep[x], dp[Val[x]]);
}

void Update(LL x)
{
	GetUp( x );
	
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		Update( to );
	}
}

inline void Get(LL x)
{
	if ( dp[Val[x]] )
		Maxx = Max(Maxx, Dep[x] + dp[Val[x]] - More);
		
	for (Int i = 0; i < 22; ++ i)
		if (dp[(1 << i) ^ Val[x]])
			Maxx = Max(Maxx, Dep[x] + dp[(1 << i) ^ Val[x]] - More);
}

void Calc(LL x)
{
	Get( x );
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
		Calc( G[x][i] );
}

void Clear(LL x)
{
	dp[Val[x]] = 0;
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
		Clear( G[x][i] );
}

void Dfs(LL x,bool Zez)
{
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		if (to != Heavy[x])
			Dfs(to, 0);
	} // Go light sons
	
	if ( Heavy[x] )
		Dfs(Heavy[x], 1); // Go heavy son
		
	More = Dep[x] << 1;
	
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		Maxx = Max(Maxx, Ans[to]); // Merge son's answer
	}
	
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		if (to != Heavy[x])
		{
			Calc( to );
			Update( to );
		}
	} // Get son-tree's answer
	
	Get( x ); GetUp( x ); // Update x's answer 
	Ans[x] = Maxx;
	
	if (! Zez)
	{
		Clear( x );
		Maxx = 0;
	}
}

int main()
{
	
	read( n );
	for (Int i = 2; i <= n; ++ i)
	{
		LL u;
		read( u );
		G[u].push_back( i );
		char c;
		scanf("%c", &c);
		Val[i] = c - 'a';
	}
	
	Dep[1] = 1;
	Prepare( 1 );
	
	Dfs(1, 1);
	
	for (Int i = 1; i <= n; ++ i)
		printf("%lld ", Ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值