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; }