您的位置:首页 > 产品设计 > UI/UE

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

2016-03-27 23:39 405 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: