题目
n个以1为根的树,每个节点上有一个字母,m次询问,问以x为根的子树中深度为y的节点,用这些节点上的字母来组成一个单词,假如这个字母可以是回文串,则输出Yes,否则输出No
题解
用二进制数存储x节点子树中单词的奇偶,奇为1,偶为0,这是一个26位的二进制数,只要这个二进制数为0或者只有一位为1,那就可以形成回文串
其他的就是dsu on tree 的方法,先dfs一遍,建立每个节点的深度,再dfs一遍,非重儿子计算过后,将二进制数数组删除,重儿子就相反,保存数组
代码
#include<iostream>
#include<vector>
#define ll long long
using namespace std;
const int maxn=6e5+10;
int a[maxn],son[maxn],sz[maxn],head[maxn],dep[maxn],vis[maxn],ans[maxn],cnt;
int h[maxn];
struct E
{
int to,nxt;
}edge[maxn];
vector<pair<int,int>> que[maxn];
void add_edge(int x,int y)
{
edge[++cnt].nxt=head[x];
edge[cnt].to=y;
head[x]=cnt;
}
bool judge(int m)
{
if(m==0) return 1;
else{
for(int i=0;i<26;i++){
if(m==(1<<i)) return 1;
}
return 0;
}
}
void dfs(int x,int f)
{
dep[x]=dep[f]+1;sz[x]=sz[f]+1;
for(int i=head[x];i;i=edge[i].nxt){
int v=edge[i].to;
dfs(v,x);
sz[x]+=sz[v];
if(!son[x]||sz[v]>sz[son[x]]) son[x]=v;
}
}
void cal(int x)
{
h[dep[x]]^=(1<<a[x]);
for(int i=head[x];i;i=edge[i].nxt){
int v=edge[i].to;
if(!vis[v]) cal(v);
}
}
void dsu(int x,int p)
{
for(int i=head[x];i;i=edge[i].nxt){
int v=edge[i].to;
if(v!=son[x]) dsu(v,0);
}
if(son[x]) dsu(son[x],1),vis[son[x]]=1;
cal(x);
for(auto i:que[x]){
ans[i.first]=judge(h[i.second]);
}
if(son[x]) vis[son[x]]=0;
if(!p) cal(x);
}
int main()
{
int n,m,x,y;
scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++){
scanf("%d",&x);
add_edge(x,i);
}
char s[maxn];
scanf("%s", s + 1);
for (int i = 1; i <= n; i++) a[i] = s[i] - 'a';
pair<int,int> A;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
A.first=i;A.second=y;
que[x].push_back(A);
}
dfs(1,0);
dsu(1,0);
for(int i=1;i<=m;i++){
if(ans[i]){
printf("Yes\n");
}
else{
printf("No\n");
}
}
}