CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(利用二进制处理回文串+dsu on tree)

传送门

在这里插入图片描述

每条边是一个字母,且最多22种,我们可以转化成一个二进制数,那么一条简单路径如果能重排得到回文串就等价为边权异或结果为2的幂数。(二进制下最多含有1个1)

带边权路径问题,我们除了点分治之外可以枚举LCA,然后在LCA的子树中求解,再利用dsu on tree优化时间复杂度。

最终把问题转化成:dis[i]表示根节点(1)到节点i的路径上边的异或结果,dep[i]表示i的深度,求满足dis[i] XOR dis[j] ==(2的幂数)的情况下dep[i]+dep[j]-2*dep[LCA]的最大值。 而2的幂数,在本题中一共只有23种,枚举一遍即可。

枚举子树根节点作为LCA,dsu on tree。处理完一棵子树的答案再更新信息。

如何处理某棵子树的答案呢?
dp[i]表示dis[u]==i的节点中,最大的深度。维护它就行。

以rt为根的子树,答案来源有三种:
①从rt出发到达孩子子树中的路径
②经过了rt,存在于两棵孩子子树的路径
③与rt无关,单独存在于某棵孩子子树的答案

三者务必枚举完全并取max。

时间复杂度23*O(nlogn)

PS:因为没有事先想清楚答案的来源,导致漏了第三种情况,wa了一晚上…
属实是学傻了捏。

以后遇到这种涉及LCA,复杂一点的,一定要先想明白再开始写。

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 5e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e17+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//有根树 每条边有一个字符 'a'~'v' 每颗子树询问 这颗子树中 最长的简单路径 经过的字符重排可得回文串
//字符压缩成2进制串 异或结果为2的幂 或者 0
//dis[u]^dis[v]=2^x/0 dep[u]+dep[v]-2dep[lca]最大
int n;
int max_dep[1<<22];//异或结果为i的 最大的dep dis数组的上界是(1<<22 -1)
int dep[maxn],dis[maxn];
vector<pii> g[maxn];
int in[maxn],clk,sz[maxn],son[maxn],fa[maxn],pos[maxn];
int ans[maxn];//每个点的答案 与rt无关纯子树中  由rt出发的路径  经过rt的路径
void dfs(int rt)
{
    
    
	sz[rt]=1;
	in[rt]=++clk;pos[clk]=rt;
	dep[rt]=dep[fa[rt]]+1;
	for(pii i:g[rt]) 
	{
    
    
		dis[i.first]=dis[rt]^i.second;
		dfs(i.first);
		sz[rt]+=sz[i.first];
		if(sz[i.first] > sz[son[rt]]) son[rt]=i.first;
	}
}
int lca;
inline void solve(int u)//统计这个点的贡献
{
    
    
	//0
	int sta=dis[u];
	if(max_dep[sta])
		ans[lca]=max(ans[lca],dep[u]+max_dep[sta]-2*dep[lca]);
	for(int i=1;i<=22;i++)
	{
    
    
		int sta=dis[u]^(1<<(i-1));
		if(max_dep[sta])
			ans[lca]=max(ans[lca],dep[u]+max_dep[sta]-2*dep[lca]);
	}
}
inline void upd(int u)//加入u 维护max_dep
{
    
    
	max_dep[dis[u]]=max(max_dep[dis[u]],dep[u]);
}
inline void add(int rt)//先统计 再维护
{
    
    
	for(int i=in[rt];i<in[rt]+sz[rt];i++)
	{
    
    
		int u=pos[i];
		solve(u);
	}
	for(int i=in[rt];i<in[rt]+sz[rt];i++)
	{
    
    
		int u=pos[i];
		upd(u);
	}
}
void dfs1(int rt,bool save) 
{
    
    
	for(pii i:g[rt]) 
	{
    
    
		if(i.first==son[rt]) continue;
		dfs1(i.first,0);
		ans[rt]=max(ans[rt],ans[i.first]);
	}
	if(son[rt]) dfs1(son[rt],1),ans[rt]=max(ans[rt],ans[son[rt]]);
	lca=rt;
	//重儿子保留信息
	solve(rt);//rt->重儿子的路径
	upd(rt);
	for(pii i:g[rt]) 
	{
    
    
		if(i.first==son[rt]) continue;
		add(i.first);
	}
	if(!save) //全部清空
	{
    
    
		for(int i=in[rt];i<in[rt]+sz[rt];i++)
		{
    
    
			int u=pos[i];
			max_dep[dis[u]]=0;
		}
	}
}
int main()
{
    
    
	scanf("%d",&n);
	for(int i=2;i<=n;i++)
	{
    
    
		char ch;
		scanf("%d %c",&fa[i],&ch);
		g[fa[i]].push_back({
    
    i,1<<(ch-'a')});
	}
	dfs(1);
	dfs1(1,1);
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/120749812