请问这题可否 维护树上每一层的字符的前缀和,然后每次询问倍增往下跳,找到它子树里要求的那层的最左边的点和最右边的点,再用前缀和判断字符出现次数奇偶性来求解?
蒟蒻写了一个,然后 #5 RE,望大佬帮忙康康。
谢谢 QwQ
#include <bits/stdc++.h>
using namespace std;
int n, m;
int u, d;
vector <int> tree[500005];
string a;
int dep[500005];
int p[500005];
int l[500005][32];
int r[500005][32];
struct node{
int s[26];
};
vector <node> sum[500005];
node make_node(int u)
{
node temp;
for(int i=0; i<26; i++) temp.s[i] = 0;
if('a' <= a[u] && a[u] <= 'z') temp.s[a[u]-'a'] = 1;
return temp;
}
node add(node a, node b)
{
for(int i=0; i<26; i++) a.s[i] += b.s[i];
return a;
}
void init(int u, int fa)
{
dep[u] = dep[fa] + 1;
if(sum[dep[u]].size() == 0) sum[dep[u]].push_back(make_node(0));
sum[dep[u]].push_back(add(sum[dep[u]][sum[dep[u]].size()-1], make_node(u)));
p[u] = sum[dep[u]].size() - 1;
for(int i=0; i<tree[u].size(); i++) init(tree[u][i], u);
if(tree[u].size() > 0){
l[u][0] = tree[u][0];
r[u][0] = tree[u][tree[u].size()-1];
}
for(int j=1; j<31; j++){
l[u][j] = l[l[u][j-1]][j-1];
r[u][j] = r[r[u][j-1]][j-1];
}
}
bool query(int u, int d)
{
int pl = u, pr = u;
int temp = 0;
d -= dep[u];
for(int i=30; i>=0; i--){
if((1 << i) <= d){
d -= (1 << i);
pl = l[pl][i];
pr = r[pr][i];
}
}
for(int i=0; i<26; i++){
if((sum[dep[pr]][p[pr]].s[i] - sum[dep[pl]][p[pl]-1].s[i]) % 2 == 1){
temp++;
}
}
return temp <= 1;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i=2; i<=n; i++){
scanf("%d", &u);
tree[u].push_back(i);
}
getchar();
getline(cin, a);
a = " " + a;
init(1, 0);
while(m--){
scanf("%d%d", &u, &d);
puts(query(u, d) ? "Yes" : "No");
}
return 0;
}
//前缀和 + 倍增