题意:给定一个以1为根的n个节点的树,每个点上有一个字母\((a-z)\),每个点的深度定义为该节点到1号节点路径上的点数.每次询问 \(a,b\) 查询以\(a\)为根的子树内深度为\(b\)的节点上的字母重新排列之后是否能构成回文串.\(n,m<=500000.\)
分析:还是那个模板,然后一个子树内的字母重新排列之后要能够构成回文串,当且仅当出现次数为奇数个的字母至多只有一个.根据这个性质,我们可以用二进制位来表示每个字母,即每个节点赋上权值\(1<<(ch-'a')\),然后合并的时候就把权值异或起来就好了.最后就只要计算这个数是否只有最多一位为1就好了.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=500005;
int n,m,visit[N],ans[N];char s[N];
int size[N],son[N],sum[N],val[N],dep[N];
int tot,head[N],nxt[N],to[N];
inline void add(int a,int b){
nxt[++tot]=head[a];head[a]=tot;to[tot]=b;
}
struct node{int h,nxt;}a[N];int Head[N];
inline void Add(int v,int h,int id){
a[id]=(node){h,Head[v]};Head[v]=id;
}
inline void pre_dfs(int u){
size[u]=1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];dep[v]=dep[u]+1;
pre_dfs(v);size[u]+=size[v];
if(size[v]>size[son[u]])son[u]=v;
}
}
inline void update(int u){
sum[dep[u]]^=val[u];//异或和a
for(int i=head[u];i;i=nxt[i]){
int v=to[i];if(visit[v])continue;
update(v);
}
}
inline bool calc(int x){//计算这个数为1的位有多少个
int cnt=0;
for(int i=0;i<=25;++i)if(x&(1<<i))++cnt;
return cnt<=1;
}
inline void dfs(int u,int keep){
for(int i=head[u];i;i=nxt[i]){
int v=to[i];if(son[u]==v)continue;
dfs(v,0);
}
if(son[u])dfs(son[u],1),visit[son[u]]=1;update(u);
for(int i=Head[u];i;i=a[i].nxt)ans[i]=calc(sum[a[i].h]);
visit[son[u]]=0;if(!keep)update(u);
}
int main(){
n=read(),m=read();
for(int i=2;i<=n;++i){int x=read();add(x,i);}//直接建有向边
scanf("%s",s+1);for(int i=1;i<=n;++i)val[i]=1<<(s[i]-'a');//给节点赋权值
for(int i=1,v,h;i<=m;++i)v=read(),h=read(),Add(v,h,i);//把询问离线
dep[1]=1;//刚开始这里没赋值,调了两个小时
pre_dfs(1);dfs(1,1);for(int i=1;i<=m;++i)puts(ans[i]?"Yes":"No");
return 0;
}