ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

741D.Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(树上启发式合并+状压)

2021-04-01 22:02:34  阅读:153  来源: 互联网

标签:paths Dokhtar 741D int son maxn first 节点 dis


只有22种字符。

一个串满足答案的条件是:

每个字符出现次数都为偶数或者出现次数为奇数的字符仅有一个。

因此我们可以考虑:

用0表示这个字符出现次数为偶数,1表示出现次数为奇数。

而对于一个路径上的字符,我们只需要考虑每个字符的出现次数,这样我们用一个整数就可以表示出所有符合条件的状态:0或者(1<<i),0<=i<=21,总共22种。

之后我们可以考虑维护一个值\(dist_i\)表示从i到1的路径上字符的状态,其实利用异或的性质\(dist_i\)就是1到i路径里面所有字符表示的字符异或的答案。

同时我们如果知道两个点,那么两个点路径上的状态就是\(dis_u\) ^\(dis_v\)。

如果两个节点之间形成的路径符合条件的话,那么\(dis_u\)^\(dis_v=0|(1<<i),0 \leq i \leq 21\)。

用\(f_x\)表示从根节点触发路径异或之后的值为\(x\)的最大深度。

然后开始DSU On Tree,先处理出每个点的重儿子,然后轻儿子和重儿子依次合并。

时间复杂度\(O(nlogn)\)。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+100;

vector<pair<int,int> > g[maxn];
int tot;
int L[maxn];//每个点进栈的时间
int R[maxn];//每个点出栈的时间
int id[maxn];//每个DFS序对应的点
int son[maxn];//重儿子
int f[maxn*20];//f(i)表示以当前节点为根的子树中状态为i的最大深度
int dis[maxn];//dis(i)表示以当前节点为根的子树中状态为i的最大深度
int ans[maxn];//记录每个点的答案
int size[maxn];//子树大小 
int dep[maxn];//层数 


void dfs1 (int x,int pre) {
	//处理重儿子
	dep[x]=dep[pre]+1;//处理出当前节点所在层数 
	size[x]=1;//处理出子树大小 
	L[x]=++tot;//DFS时入栈的时间点 
	id[tot]=x;//DFS序上节点本来的编号 
	for (pair<int,int> y:g[x]) {
		dis[y.first]=dis[x]^y.second;//dis[y]记录根节点到节点外的边权异或和 
		dfs1(y.first,x);//往下搜 
		size[x]+=size[y.first];//更新子树 
		if (size[son[x]]<size[y.first]) son[x]=y.first;//记录重儿子 
	}
	R[x]=tot;//记录出栈时间点 
}
void cal (int x) {
	//这里f(dis(x))表示在以x为根的子树状态中状态为dis(x)的最大深度
	if (f[dis[x]]) ans[x]=max(ans[x],f[dis[x]]-dep[x]);//如果该节点的子树内有路径异或和为dis[x]的点,那么就先更新长度 
	for (int i=0;i<22;i++) {
		if (f[dis[x]^(1<<i)]) {
			ans[x]=max(ans[x],f[dis[x]^(1<<i)]-dep[x]);//同理,这里记录的是奇数长度回文串 
		}
	}
	f[dis[x]]=max(f[dis[x]],dep[x]);//f[dis[x]]初始化为自己 
	
	for (pair<int,int> y:g[x]) {//遍历点x的每个儿子
		if (y.first==son[x]) continue;//不遍历x的重儿子 
		for (int j=L[y.first];j<=R[y.first];j++) {
			int z=id[j];
			//考虑以x为中间点的情况 
			//遍历整个y子树
			//如果f(dis(z))可以和别的子树里的路径产生答案,那么更新答案
			if (f[dis[z]]) ans[x]=max(ans[x],f[dis[z]]+dep[z]-2*dep[x]);
			
			for (int k=0;k<22;k++) {
				if (f[dis[z]^(1<<k)]) {
					ans[x]=max(ans[x],f[dis[z]^(1<<k)]+dep[z]-2*dep[x]);
				}
			} 
		}
		for (int j=L[y.first];j<=R[y.first];j++) {
			f[dis[id[j]]]=max(f[dis[id[j]]],dep[id[j]]); 
			//同时把子树里的每个点的贡献统计 
		}
	} 
}

void dfs2 (int x,int kp) {
	//kp为1表示处理并保留数据
	//否则表示处理,否则表示处理但不保留数据
	for (pair<int,int> y:g[x]) {
		if (y.first==son[x]) continue;
		dfs2(y.first,0);
		ans[x]=max(ans[x],ans[y.first]);
		//更新答案,因为有可能最大长度的链不以当前节点为根节点 
	}  
	
	if (son[x]) {
		dfs2(son[x],1);
		ans[x]=max(ans[x],ans[son[x]]);
	}
	cal(x);//统计节点x的答案 
	if (!kp) for (int i=L[x];i<=R[x];i++) f[dis[id[i]]]=0;
	//如果是轻儿子,则清空数据
	//如果是重儿子,就不清空,保留数据,后续把轻儿子往重儿子上合并,时间复杂度的优化主要在这里 
}
int main () {
	int n;
	scanf("%d",&n);
	for (int i=2;i<=n;i++) {
		int x;
		char ch;
		scanf("%d %c",&x,&ch);
		g[x].push_back(make_pair(i,1ll<<(ch-'a')));
	}
	dfs1(1,0);
	dfs2(1,1);
	for (int i=1;i<=n;i++) printf("%d ",ans[i]);
}

标签:paths,Dokhtar,741D,int,son,maxn,first,节点,dis
来源: https://www.cnblogs.com/zhanglichen/p/14608387.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有