CF570D - Tree Requests 树上启发式合并

CF570D - Tree Requests

树上启发式合并学习笔记

题意

N N 个点以 1 1 为根的树,每个节点都有对应的字母 s [ i ] s[i] M M 次查询,每次判断以 u i u_i 为根的子树中,深度为 h i h_i 的节点能否组成一个回文串(字母顺序可以任意改变),如果能输出Yes,否则输出No

分析

树上启发式合并入门题,查询的子树信息就是对应高度都有什么字母
因为这里节点的顺序能够改变,所以构成回文串只需要满足:每一种字母都出现偶数次或者至多只有一个字母是奇数次即可,如abba, abcba
因为全是小写字母,只有26个,所以我用异或值来表示每一个深度的状态 f [ i ] f[i] ,如现在深度 i i 添加一个字母 c c ,那么 f [ i ] f[i] ^= ( 1 < < ( c a ) ) (1<<(c-a)) ,这样每一层的信息都压缩了,我们只需要判断那一层的值是不是刚好是 2 i 2^i 或是0即可
对查询做离线处理,每一个点用一个pair<int, int>数组来记录要查什么深度和询问的 i d id ,这样到达对应节点直接询问答案即可

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/weixin_44282912/article/details/104590631
今日推荐