题目链接:https://codeforces.com/problemset/problem/570/D
题解:
这种题,基本上容易想到DFS序。
然后,我们如果再把所有节点分层存下来,那么显然可以根据 $in[v],out[v]$ 在层内二分出一段属于 $v$ 的子树的节点。
那么我们进一步考虑,如果把一层的节点,按 $a \sim z$ 再分开来,用一个 $S[c][d]$ 数组来存所有字母为 $c$,深度为 $d$ 的节点的 $in[]$ 值。
这样一来,对于一个询问 $v,h$,就在 $S[c][h]$ 二分找到属于区间 $[in[v],out[v]]$ 的那一段,这一段的长度如果为 $r$,说明 $v$ 节点的子树中、深度为 $h$ 的、字母为 $c$ 的节点有 $r$ 个,
这个 $r$ 若为偶数,那么必然可以用来组成回文串;如果为奇数,那么最多只能一个字母的个数是奇数,否则就不能组成回文串了。
AC代码:
#include<bits/stdc++.h>
#define pb(x) push_back(x)
using namespace std;
typedef pair<int,int> P;
#define fi first
#define se second
const int maxn=5e5+; int n,m;
char c[maxn];
int d[maxn];
vector<int> G[maxn];
vector<int> S[][maxn]; int clk;
int maxd;
int in[maxn],out[maxn];
void dfs(int x,int depth)
{
in[x]=++clk;
maxd=max(maxd,depth);
S[c[x]-'a'][d[x]=depth].pb(in[x]);
for(auto y:G[x]) dfs(y,depth+);
out[x]=clk;
} int main()
{
scanf("%d%d",&n,&m);
for(int y=,x;y<=n;y++)
{
scanf("%d",&x);
G[x].pb(y);
}
scanf("%s",c+); clk=, maxd=, dfs(,); while(m--)
{
int v,h; scanf("%d%d",&v,&h);
if(d[v]>=h)
{
printf("Yes\n");
continue;
} int cnt=;
for(int i=;i<;i++)
{
int L=lower_bound(S[i][h].begin(),S[i][h].end(),in[v])-S[i][h].begin();
int R=upper_bound(S[i][h].begin(),S[i][h].end(),out[v])-S[i][h].begin();
cnt+=(R-L)%;
}
if(cnt>) printf("No\n");
else printf("Yes\n");
}
}