cf#316-codeforces570D - Tree Requests -dfs序+分类再二分+树dp+异或位运算+bitmask(位压缩)

http://codeforces.com/contest/570/problem/D

题意:给一棵树n个节点,每个节点有一个字母信息。

节点1的深度为1,其他节点的深度 为 到1的距离

m次查询,每次查询 给出(v,h)  求以v为根节点的子树的所有节点中  深度在第 h 层 的所有节点 能否构成一个回文串(以任意顺序排列)


首先既然是任意顺序构成回文串的话,只要是该字母出现偶数次就没问题,我们只需要看 出现奇数次的字母 的个数就好,只有个数为0或1才能构成回文串。


字母只有26个,我们可以用一个int的二进制位来表示。(位压缩)


首先我们跑一个dfs序,并将某个dfs序号相应的节点编号 对应起来,

然后 把所有的dfs编号 按照深度 存到 deep[depth][i]里

把deep【i】按 dfs序号排序,并且计算一个 前缀异或和 (mark[i]表示前i个序号对应的节点的字母信息的异或和  (字母已经映射为  1<<i ,例如 a为1,b为1<<1,z为1<<25))

对于每次查询(v,h),然后这个v节点的所有子节点的 dfs序号肯定在 in[v ]- out[v] 之间,

然后对于深度为h的子孙节点的dfs序号 ,必然都存在deep[h]啦,并且肯定是连续的

于是我们可以二分找到in[v],out[v]对应的端点,然后  取前缀异或和相 异或,前面说了,两种情况才能构成回文串,如果异或结果为0,符合第一个情况,如果异或结果的二进制位只有一个1(也就是2的方幂)也是符合的 (奇回文串)

这里直接用lowbit来求,也就是x==lowbit(x),也就是x==(x&-x)



#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std; 
const __int64 N=200005;
__int64 mod=1e9+7;
double eps=1e-9;
__int64 max(__int64 a,__int64 b)
{return a<b?b:a; } 
		
vector< vector<int> > mp(500005);			//树
vector< vector<int> > deep(500005);			//deep[i]存同一深度的所有节点,按dfs序排序
vector< vector<int> > mark(500005);			//deep[i]中对应节点的前缀异或和 

int id=0;					//dfs序编号
int bit[27];				//1<<i
int in[500005],out[500005];	//dfs序列
int who[500005];		//dfs序对应的节点编号
int dep[500005];		//节点的深度
char tm[500005];		//节点的信息

void dfs(int x,int dd)
{
	in[x]=++id;
	dep[x]=dd;
	who[id]=x;
	int i;
	for (i=0;i<mp[x].size();i++)
	{
		int v=mp[x][i]; 
		dfs(v,dd+1);
	}
	out[x]=id; 
} 
int main()
{ 

	int x,i,j;
	int n,m;
	for (i=0;i<26;i++)
		bit[i]=1<<i;
	cin>>n>>m;
	for (i=2;i<=n;i++)
	{
		scanf("%d",&x);
		mp[x].push_back(i);
	} 
	dfs(1,1);
	scanf("%s",tm+1);
	for (i=1;i<=n;i++)
		deep[dep[i]].push_back(in[i]);		
	for (i=1;i<=n;i++)
	sort(deep[i].begin(),deep[i].end());		//按dfs序排序
	for (i=1;i<=n;i++)
	{
		for (j=0;j<deep[i].size();j++)
		{
			int tmp=who[deep[i][j]];
			tmp=tm[tmp]-'a';
			if (!j) mark[i].push_back(bit[tmp]);	//第一个
			else
			{
				int last=mark[i].back();
				mark[i].push_back(last^bit[tmp]);	//与前面的异或前缀和异或
			}
		}
	}
	int v,h;
	for (i=1;i<=m;i++)
	{
		scanf("%d%d",&v,&h);
		int st=in[v];
		int ed=out[v];
		int it=lower_bound(deep[h].begin(),deep[h].end(),st)-deep[h].begin();
		int it2=upper_bound(deep[h].begin(),deep[h].end(),ed)-deep[h].begin();
		it--,it2--;			//二分找到v节点对应的dfs序起始编号
		int tmp1=0,tmp2=0;
		if (it>=0) tmp1=mark[h][it];
		if (it2>=0) tmp2=mark[h][it2];		
		int ans=tmp1^tmp2;				//对应结果异或,得到从st到ed的所有节点的异或结果
		if (!ans||ans==(ans&(-ans)))	//如果只有一个字母个数为奇数,或者全为偶数,必构成回文串。
			printf("Yes\n");
		else
			printf("No\n");  
		
	}
	
	
	return 0;
}









你可能感兴趣的:(cf#316-codeforces570D - Tree Requests -dfs序+分类再二分+树dp+异或位运算+bitmask(位压缩))