CF741D

树上启发式合并题。

首先,要找到判断一个字符集是否存在重构成回文串的方法。

能重组成回文串仅当只存在至多一种字符的出现次数为奇数。

我们令 $a_u$ 表示 $1\to u$ 路径上的字符集的二进制状态。具体的,从右往左数第 $1$ 位表示字符 $a$ 的出现次数是否为奇数;从右往左第 $2$ 位表示字符 $b$ 的出现次数是否为奇数……以此类推。

我们发现,祖先 $p$ 到 $u$ 路径上的二进制状态等价于 $a_p\bigoplus a_u$。也就是任意点对 $(u,v)$ 路径上的二进制状态等价于 $(a_u\bigoplus a_{lca})\bigoplus (a_v\bigoplus a_{lca})=a_u\bigoplus a_v$。

这时我们就有方法统计答案的最大值了。点 $u$ 的答案等价于经过 $u$ 的最长合法路径的长度,以及其子节点的答案的最大值。维护经过 $u$ 的最长合法路径只需要维护所有的 $a_i$,之后直接树上启发式合并即可。

这题的启发式合并过程中,先遍历轻儿子,最后重儿子。轻儿子的信息清空,重儿子的不清空。对于一颗子树先查询再修改。查询:由于允许至多一种字符出现次数为奇数,所以就枚举哪种字符出现次数为奇数(或者没有字符出现次数为奇数)。

#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
const int N = 5e5 + 10, INF = 1e9;
int n, a[N], sz[N], son[N], ans[N], dep[N], cnt[1 << 22];
vector<pair<int, int> > e[N];
void dfs(int u){
    sz[u] = 1;
    for(auto &p: e[u]){
        int v = p.first, w = p.second;
        a[p.first] = a[u] ^ (1 << w);
        dep[v] = dep[u] + 1, dfs(v), sz[u] += sz[v];
        if(sz[v] > sz[son[u]]) son[u] = v;
    }
}
void Add(int u){
    cnt[a[u]] = max(cnt[a[u]], dep[u]);
    for(auto &p: e[u]) Add(p.first);
}
void Del(int u){
    cnt[a[u]] = -INF;
    for(auto &p: e[u]) Del(p.first);
}
int calc(int u, int rt){
    int ret = max(0, dep[u] + cnt[a[u]]);
    FL(i, 0, 21) ret = max(ret, dep[u] + cnt[a[u] ^ (1 << i)]);
    if(u == rt) cnt[a[u]] = max(cnt[a[u]], dep[u]);
    for(auto &p: e[u]) if(p.first != son[rt]){
        ret = max(ret, calc(p.first, rt));
        if(u == rt) Add(p.first);
    }
    return ret;
}
void solve(int u, int h){
    for(auto &p: e[u])
        if(p.first != son[u]) solve(p.first, 0);
    if(son[u]) solve(son[u], 1);
    ans[u] = calc(u, u), ans[u] = max(0, ans[u] - dep[u] * 2);
    for(auto &p: e[u]) ans[u] = max(ans[u], ans[p.first]);
    if(!h) Del(u);
}
int main(){
    scanf("%d", &n);
    FL(i, 0, (1 << 22) - 1) cnt[i] = -INF;
    FL(i, 2, n){
        int p; char c;
        scanf("%d %c", &p, &c);
        e[p].push_back({i, c - 'a'});
    }
    dfs(1), solve(1, 0);
    FL(i, 1, n) printf("%d ", ans[i]);
    return 0;
}
posted @ 2023-07-17 20:44  徐子洋  阅读(11)  评论(0)    收藏  举报  来源