Codeforces 914E 点分治

题意

传送门 Codeforces 914E Palindromes in a Tree

题解

一个字符串存在一个回文串排列,当且仅当字符串中出现次数为奇数的字符不超过一个。以 v v v 为根, a u a_u au 代表根到节点 u u u 间字母的奇偶性,则通过 v v v 的路径 u → w u\rightarrow w uw 可以表为 a u ⊕ a w ⊕ a v a_u\oplus a_w\oplus a_v auawav

考虑点分治。每次处理仅考虑通过重心的路径。对于重心,依次考虑子树贡献。对于非重心 u u u,这样的路径的一个端点必然在 u u u 的子树内,递归相加即可。总时间复杂度 O ( 20 n log ⁡ n ) O(20n\log n) O(20nlogn)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int C = 20;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    vector<vector<int>> g(n);
    for (int i = 0; i < n - 1; ++i) {
        int u, v;
        cin >> u >> v;
        --u, --v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    string s;
    cin >> s;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; ++i) {
        a[i] = 1 << (s[i] - 'a');
    }

    vector<int> sz(n), del(n);
    vector<int> cnt(1 << C);
    vector<ll> res(n, 1);
    function<void(int)> solve = [&](int v) {
        function<void(int, int)> get_sz = [&](int v, int p) {
            sz[v] = 1;
            for (int u : g[v]) {
                if (u == p || del[u]) {
                    continue;
                }
                get_sz(u, v);
                sz[v] += sz[u];
            }
        };
        get_sz(v, -1);
        for (int lim = sz[v] / 2;;) {
            int mv = 0;
            for (int u : g[v]) {
                if (!del[u] && sz[u] < sz[v] && sz[u] > lim) {
                    mv = 1;
                    v = u;
                    break;
                }
            }
            if (!mv) {
                break;
            }
        }
        function<void(int, int)> get_b = [&](int v, int p) {
            for (int u : g[v]) {
                if (del[u] || u == p) {
                    continue;
                }
                b[u] = b[v] ^ a[u];
                get_b(u, v);
            }
        };
        b[v] = a[v];
        get_b(v, -1);
        function<void(int, int, int)> add = [&](int v, int p, int d) {
            cnt[b[v]] += d;
            for (int u : g[v]) {
                if (del[u] || u == p) {
                    continue;
                }
                add(u, v, d);
            }
        };
        auto get = [&](int x) {
            ll t = cnt[x];
            for (int i = 0; i < C; ++i) {
                t += cnt[x ^ (1 << i)];
            }
            return t;
        };
        function<ll(int, int, int)> count = [&](int v, int p, int d) {
            ll t = get(b[v] ^ d);
            for (int u : g[v]) {
                if (del[u] || u == p) {
                    continue;
                }
                t += count(u, v, d);
            }
            res[v] += t;
            return t;
        };
        add(v, -1, 1);
        ll t = 0;
        for (int u : g[v]) {
            if (del[u]) {
                continue;
            }
            add(u, v, -1);
            t += count(u, v, b[v]);
            add(u, v, 1);
        }
        cnt[b[v]] -= 1;
        t += get(0);
        res[v] += t / 2;
        cnt[b[v]] += 1;
        add(v, -1, -1);
        del[v] = 1;
        for (int u : g[v]) {
            if (del[u]) {
                continue;
            }
            solve(u);
        }
    };
    solve(0);

    for (int i = 0; i < n; ++i) {
        cout << res[i] << " \n"[i + 1 == n];
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值