NC213912 芭芭拉冲鸭~(续)

发布时间 2023-06-23 17:03:23作者: 空白菌

题目链接

题目

题目描述

芭芭拉这次来到了一棵字母树,这同样是一棵无根树,每个节点上面有一个小写字母。

芭芭拉想知道,自己从x冲刺到y,从x走到y收集所有字母,选择其中一部分字母组成一个回文串,这个回文串的最大长度是多少?

同样的,芭芭拉冲刺的时候是不能掉头的。

一共有q次询问。每次的询问是独立的(即本次收集字母不影响之后的询问,每次询问时字母都是未被收集状态)。

输入描述

第一行有一个正整数 $ n\ $ 。
接下来的 $ n-1\ $ 行,每行输入两个正整数 $ x\ $ 和 $ y\ $ ,代表 $ x\ $ 和 $ y\ $ 之间有一条无向边相连。
接下来一行有一个长度为 $ n\ $ 的字符串,字符串仅由小写字母构成。第 $ i\ $ 个字符表示节点 $ i\ $ 上的字母。
接下来一行是一个正整数 $ q\ $ ,代表询问次数。

接下来的 $ q\ $ 行,每行两个正整数 $ x\ $ 和 $ y\ $ 。

(保证输入一定是一棵树)

$(1≤n,q≤100000,1≤x,y≤n) \ $

输出描述

对应每次询问,输出一个正整数,代表回文串的最大长度。

示例1

输入

5
1 2
1 3
2 4
2 5
abcba
3
4 5
1 2
3 3

输出

3
1
1

说明

这棵树的构造如下:

对于第一个询问,芭芭拉冲刺的路径是4-2-5,收集的字母有两个b一个a,可以构建的最长回文串是"bab",长度为3。
对于第二个询问,芭芭拉冲刺的路径是1-2,收集的字母有一个b一个a,可以构建的最长回文串是"a"(也可以是"b"),长度为1。
对于第三个询问,芭芭拉起点和终点都是3,所以站在原地不动,收集的字母有只有一个c,可以构建的最长回文串是"c",长度为1。

题解

知识点:树链剖分,枚举,前缀和。

考虑用树剖,然后用前缀和维护一条树链的各个字母的数量。

每次询问时,求出路径字母数量和后,构造最长的回文串即可:偶数字母直接加,奇数字母只能完整加一次,剩下的只能减 \(1\)

时间复杂度 \(O(n + q\log n)\)

空间复杂度 \(O(n)\)

代码

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

struct HLD {
    vector<int> siz, dep, fat, son, top, dfn, L, R;

    HLD() {}
    HLD(int rt, const vector<vector<int>> &g) { init(rt, g); }

    void init(int rt, const vector<vector<int>> &g) {
        assert(g.size() >= 2);
        int n = g.size() - 1;
        siz.assign(n + 1, 0);
        dep.assign(n + 1, 0);
        fat.assign(n + 1, 0);
        son.assign(n + 1, 0);
        top.assign(n + 1, 0);
        dfn.assign(n + 1, 0);
        L.assign(n + 1, 0);
        R.assign(n + 1, 0);

        function<void(int, int)> dfsA = [&](int u, int fa) {
            siz[u] = 1;
            dep[u] = dep[fa] + 1;
            fat[u] = fa;
            for (auto v : g[u]) {
                if (v == fa) continue;
                dfsA(v, u);
                siz[u] += siz[v];
                if (siz[v] > siz[son[u]]) son[u] = v;
            }
        };
        dfsA(rt, 0);

        int dfncnt = 0;
        function<void(int, int)> dfsB = [&](int u, int tp) {
            top[u] = tp;
            dfn[++dfncnt] = u;
            L[u] = dfncnt;
            if (son[u]) dfsB(son[u], tp);
            for (auto v : g[u]) {
                if (v == fat[u] || v == son[u]) continue;
                dfsB(v, v);
            }
            R[u] = dfncnt;
        };
        dfsB(rt, rt);
    }
};

const int N = 100007;
vector<int> g[N];
HLD hld;
int sum[N][26];

int path_query(int u, int v) {
    auto &top = hld.top;
    auto &dep = hld.dep;
    auto &fat = hld.fat;
    auto &L = hld.L;
    int cnt[26] = { 0 };
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        for (int i = 0;i < 26;i++) cnt[i] += sum[L[u]][i] - sum[L[top[u]] - 1][i];
        u = fat[top[u]];
    }
    if (dep[u] > dep[v]) swap(u, v);
    for (int i = 0;i < 26;i++) cnt[i] += sum[L[v]][i] - sum[L[u] - 1][i];

    int ans = 0;
    bool odd = 0;
    for (int i = 0;i < 26;i++) {
        ans += cnt[i] - ((cnt[i] & 1) && odd);
        odd |= cnt[i] & 1;
    }
    return ans;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1;i <= n - 1;i++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    hld.init(1, vector<vector<int>>(g, g + n + 1));

    for (int i = 1;i <= n;i++) {
        char ch;
        cin >> ch;
        sum[hld.L[i]][ch - 'a']++;
    }
    for (int i = 1;i <= n;i++)
        for (int j = 0;j < 26;j++)
            sum[i][j] += sum[i - 1][j];

    int q;
    cin >> q;
    while (q--) {
        int u, v;
        cin >> u >> v;
        cout << path_query(u, v) << '\n';
    }
    return 0;
}