萌新刚学OI,求助此题做法
查看原帖
萌新刚学OI,求助此题做法
232838
huangkx楼主2021/12/15 17:50

请问这题可否 维护树上每一层的字符的前缀和,然后每次询问倍增往下跳,找到它子树里要求的那层的最左边的点和最右边的点,再用前缀和判断字符出现次数奇偶性来求解?
蒟蒻写了一个,然后 #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;
}
//前缀和 + 倍增
2021/12/15 17:50
加载中...