CF570D - Tree Requests
题意
个点以 为根的树,每个节点都有对应的字母 , 次查询,每次判断以 为根的子树中,深度为 的节点能否组成一个回文串(字母顺序可以任意改变),如果能输出Yes,否则输出No
分析
树上启发式合并入门题,查询的子树信息就是对应高度都有什么字母
因为这里节点的顺序能够改变,所以构成回文串只需要满足:每一种字母都出现偶数次或者至多只有一个字母是奇数次即可,如abba, abcba
因为全是小写字母,只有26个,所以我用异或值来表示每一个深度的状态
,如现在深度
添加一个字母
,那么
^=
,这样每一层的信息都压缩了,我们只需要判断那一层的值是不是刚好是
或是0即可
对查询做离线处理,每一个点用一个pair<int, int>数组来记录要查什么深度和询问的
,这样到达对应节点直接询问答案即可
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
typedef pair<int, int> pii;
const int MAX = 5e5 + 10;
int N, M;
int a[MAX];
char s[MAX];
vector<int> g[MAX];
vector<pii> query[MAX];
int siz[MAX], son[MAX], dep[MAX];
void dfs(int u, int fa) {
dep[u] = dep[fa] + (siz[u] = 1);
for (auto &v: g[u])
if (v != fa) {
dfs(v, u);
siz[u] += siz[v];
if (!son[u] || siz[v] > siz[son[u]])
son[u] = v;
}
}
int vis[MAX], f[MAX], ans[MAX];
bool judge(int num) {//如果为0或者是2^i,那么就是可以构成回文串
if (!num) return true;
for (int bit = 0; bit <= 25; bit++)
if ((1 << bit) == num) return true;
return false;
}
void upd(int u, int fa) {
f[dep[u]] ^= (1 << a[u]);//每次对应的深度异或上对应的二进制数
for (auto &v: g[u])
if (v != fa && !vis[v]) upd(v, u);
}
void dsu(int u, int fa, int keep) {
for (auto &v: g[u])
if (v != fa && v != son[u]) dsu(v, u, 0);
if (son[u]) dsu(son[u], u, 1), vis[son[u]] = 1;
upd(u, fa);
for (auto &i: query[u])
ans[i.second] = judge(f[i.first]);//更新答案
if (son[u]) vis[son[u]] = 0;
if (!keep) upd(u, fa);//异或的性质,再异或一遍就是消除影响
}
int main() {
scanf("%d%d", &N, &M);
for (int i = 2; i <= N; i++) {
int u; scanf("%d", &u);
g[u].push_back(i); g[i].push_back(u);
}
scanf("%s", s + 1);
for (int i = 1; i <= N; i++) a[i] = s[i] - 'a';
for (int i = 1; i <= M; i++) {
int u, h; scanf("%d%d", &u, &h);
query[u].push_back(make_pair(h, i));//询问的深度和查询的id
}
dfs(1, 0);
dsu(1, 0, 0);
for (int i = 1; i <= M; i++)
printf("%s\n", ans[i] ? "Yes" : "No");
return 0;
}