題目
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");
}
}
}