1 条题解

  • 0
    @ 2026-10-26 1:15:48

    思路与算法

    把树以1为根,先迭代求父亲、深度和遍历顺序,再逆序求子树大小与重儿子。沿重边优先编号,记录每条重链的链头和每点在线性序列中的位置。

    在线性序列上建立线段树,每个区间保存节点权值的和与最大值。CHANGE对对应位置做单点赋值。

    查询u-v路径时,只要链头不同,就取链头更深的一侧,把链头到该点的连续编号区间合并进答案,再跳到链头父亲。两点进入同一条链后,查询其间最后一个区间。QSUM累加各段和,QMAX取各段最大值。

    正确性说明

    重链中的节点按深度连续编号,因此链上两点之间的路径等于一个序列区间。链头不同的时候,较深链头到对应端点的整段都位于原查询路径上;移除这一段并上跳到链头父亲,不会漏掉路径节点,也不会重复计入节点。

    不断移除这些段,直到剩下同链的最后一段,各段恰好构成完整u-v路径。线段树维护的区间和与最大值对单点赋值始终正确,合并所有互不重叠的路径段便得到所求统计。

    复杂度

    n点、q次操作。邻接表及所有剖分数组初始化O(n),两遍迭代剖分O(n),线段树数组初始化与建树O(n)。每次单点赋值O(log(n+1));一条路径至多拆成O(log(n+1))段,每段查询O(log(n+1)),故路径查询O(log²(n+1))。总时间O(n+q log²(n+1)),空间O(n)。

    易错点

    权值可以全部为负,最大值单位元必须是LLONG_MIN,不能是0。CHANGE是改成t,并非增加t。路径包含两个端点;同点查询只统计该点。q可以为0,此时不输出任何查询结果。剖分和父亲遍历均采用迭代实现处理最长链。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    struct HLD {
        int n, tim = 0;
        vector<vector<int>> g;
        vector<int> p, d, sz, heavy, top, id, rev, order;
        HLD(int n)
            : n(n), g(n + 1), p(n + 1), d(n + 1), sz(n + 1, 1), heavy(n + 1), top(n + 1), id(n + 1),
              rev(n + 1) {
            sz[0] = 0;
        }
        void add(int a, int b) {
            g[a].push_back(b);
            g[b].push_back(a);
        }
        void build() {
            order.push_back(1);
            for (int i = 0; i < (int)order.size(); ++i) {
                int u = order[i];
                for (int v : g[u])
                    if (v != p[u])
                        p[v] = u, d[v] = d[u] + 1, order.push_back(v);
            }
            for (int i = n - 1; i > 0; --i) {
                int u = order[i], v = p[u];
                sz[v] += sz[u];
                if (sz[u] > sz[heavy[v]])
                    heavy[v] = u;
            }
            vector<pair<int, int>> st(1, {1, 1});
            while (!st.empty()) {
                auto q = st.back();
                st.pop_back();
                for (int u = q.first; u; u = heavy[u]) {
                    top[u] = q.second;
                    id[u] = ++tim;
                    rev[tim] = u;
                    for (int v : g[u])
                        if (p[v] == u && v != heavy[u])
                            st.push_back({v, v});
                }
            }
        }
    };
    struct Seg {
        int n;
        vector<ll> s, mx;
        Seg(vector<ll> &a) : n(a.size() - 1), s(4 * n + 4), mx(4 * n + 4) {
            build(1, 1, n, a);
        }
        void pull(int k) {
            s[k] = s[k * 2] + s[k * 2 + 1];
            mx[k] = max(mx[k * 2], mx[k * 2 + 1]);
        }
        void build(int k, int l, int r, vector<ll> &a) {
            if (l == r) {
                s[k] = mx[k] = a[l];
                return;
            }
            int m = (l + r) / 2;
            build(k * 2, l, m, a);
            build(k * 2 + 1, m + 1, r, a);
            pull(k);
        }
        void setval(int k, int l, int r, int x, ll v) {
            if (l == r) {
                s[k] = mx[k] = v;
                return;
            }
            int m = (l + r) / 2;
            if (x <= m)
                setval(k * 2, l, m, x, v);
            else
                setval(k * 2 + 1, m + 1, r, x, v);
            pull(k);
        }
        pair<ll, ll> get(int k, int l, int r, int a, int b) {
            if (a <= l && r <= b)
                return {s[k], mx[k]};
            int m = (l + r) / 2;
            pair<ll, ll> x = {0, LLONG_MIN};
            if (a <= m)
                x = get(k * 2, l, m, a, b);
            if (b > m) {
                auto y = get(k * 2 + 1, m + 1, r, a, b);
                x.first += y.first;
                x.second = max(x.second, y.second);
            }
            return x;
        }
    };
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        HLD h(n);
        for (int i = 1, u, v; i < n; ++i) {
            cin >> u >> v;
            h.add(u, v);
        }
        h.build();
        vector<ll> a(n + 1);
        for (int u = 1; u <= n; ++u)
            cin >> a[h.id[u]];
        Seg st(a);
        int q;
        cin >> q;
        while (q--) {
            string op;
            int u, v;
            cin >> op >> u >> v;
            if (op == "CHANGE") {
                st.setval(1, 1, n, h.id[u], v);
                continue;
            }
            ll sum = 0, mx = LLONG_MIN;
            while (h.top[u] != h.top[v]) {
                if (h.d[h.top[u]] < h.d[h.top[v]])
                    swap(u, v);
                auto x = st.get(1, 1, n, h.id[h.top[u]], h.id[u]);
                sum += x.first;
                mx = max(mx, x.second);
                u = h.p[h.top[u]];
            }
            if (h.d[u] > h.d[v])
                swap(u, v);
            auto x = st.get(1, 1, n, h.id[u], h.id[v]);
            sum += x.first;
            mx = max(mx, x.second);
            cout << (op == "QSUM" ? sum : mx) << '\n';
        }
    }
    
    • 1

    信息

    ID
    1636
    时间
    1000ms
    内存
    512MiB
    难度
    普及+
    标签
    递交数
    1
    已通过
    1
    上传者