链接:http://codeforces.com/contest/570/problem/D
题意:给你一棵n个节点树,树的每个节点少有一个字母,然后m个询问,问节点a的子树中深度(以1为根)为b的节点上的字母能否组成回文串。
分析:判断回文串,我们只要找到字母为奇数个的个数大于1就不能组成回文,我们可以用异或求出,用前缀和优化,在dfs的时候深度一样的节点都是按照顺序便利的,我们可以用vector记录当前深度的异或和。
然后我们找子节点的时候肯定不能暴力,我们可以可以用dfs序的到子节点与根的关系,子节点的左dfs序一定大于根,右dfs序小于根,然后就可以二分求出了。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 500005
#define Mm 5000005
#define mod 1000000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#define CPY(a,b) memcpy ((a), (b), sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
struct edge {
int v,next;
}e[Mm];
int tot,head[Mn];
void addedge(int u,int v) {
e[tot].v=v;
e[tot].next=head[u];
head[u]=tot++;
}
int ldfn[Mn],rdfn[Mn];
int dep[Mn];
vector<pair<int,int> > ve[Mn];
int dfnclock=0;
string s;
void dfs(int u,int de) {
dep[u]=de;
ldfn[u]=++dfnclock;
ve[de].push_back(make_pair(dfnclock,ve[de].back().second^(1<<(s[u-1]-'a'))));
for(int i=head[u];~i;i=e[i].next) {
int v=e[i].v;
dfs(v,de+1);
}
rdfn[u]=++dfnclock;
}
int getbinum(int x) {
int re=0;
while(x) {
re++;x-=x&(-x);
}
return re;
}
void init() {
tot=0;
CLR(head,-1);
}
int main() {
int n,m,x;
init();
cin>>n>>m;
for(int i=2;i<=n;i++) {
scanf("%d",&x);
addedge(x,i);
}
cin>>s;
for(int i=1;i<=n;i++)
ve[i].push_back(make_pair(0,0));
dfs(1,1);
for(int i=1;i<=m;i++) {
int u,h;
scanf("%d%d",&u,&h);
if(dep[u]>=h) {
printf("Yes\n");
continue;
}
int lv=lower_bound(ve[h].begin(),ve[h].end(),make_pair(ldfn[u],-1))-ve[h].begin()-1;//找到的就是最左端,但是编号从1开始所以减一。
int rv=lower_bound(ve[h].begin(),ve[h].end(),make_pair(rdfn[u],-1))-ve[h].begin()-1;//找到大于的,减一就是子树的最右断,
if(getbinum(ve[h][lv].second^ve[h][rv].second)<=1) {
printf("Yes\n");
} else printf("No\n");
}
return 0;
}