1 条题解

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

    思路与算法

    树的任意两点之间只有一条简单路径,因此不必为每次询问重新运行最短路算法。

    以1为根,迭代遍历求深度depth和根距离dist,并记录父亲。倍增表up[k][v]表示v的第2^k级祖先,依公式up[k][v]=up[k-1][up[k-1][v]]填写。

    查询u、v时先把较深的点跳到相同深度;若已相同,这一点就是LCA。否则从大到小同时跳到不同的祖先,最后两点的父亲即LCA。输出dist[u]+dist[v]-2*dist[LCA]。

    正确性说明

    两条根到端点的路径共有根到最近公共祖先的一段。将两条根距离相加,会把这段公共路径计两次;减去两倍LCA根距离后,剩下恰为u到LCA和LCA到v的两段,其并集就是唯一的u-v路径。

    倍增先对齐深度,再只在祖先不同的时候同时上移,始终停留在LCA之下;结束后父亲正是两点最深的公共祖先。因此代入公式得到所求距离。同点时LCA就是该点,结果自然为0。

    复杂度

    n点、m次询问。邻接表、根距离和深度数组初始化与迭代遍历O(n),祖先表初始化及递推O(n log(n+1));每次LCA查询O(log(n+1))。总时间O(n log(n+1)+m log(n+1)),空间O(n log(n+1))。

    易错点

    输入边是双向的,建图需加入两条邻接边,并在遍历时跳过父边。边数为n-1,m表示询问数。注意读取树边长度k,而不是把每条边当1。根的祖先固定为根,便于处理跳转边界;迭代遍历可避免链形树递归过深。两组公开样例都已纳入评测。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    struct Tree {
        int n, L, timer = 0;
        vector<vector<pair<int, ll>>> g;
        vector<vector<int>> up;
        vector<int> d, tin, rev;
        vector<ll> dist;
        Tree(int n) : n(n), L(1), g(n + 1), d(n + 1), tin(n + 1), rev(n + 1), dist(n + 1) {
            while ((1 << L) <= n)
                ++L;
            up.assign(L, vector<int>(n + 1));
        }
        void add(int u, int v, ll w = 1) {
            g[u].push_back({v, w});
            g[v].push_back({u, w});
        }
        void build() {
            vector<int> st(1, 1);
            up[0][1] = 1;
            while (!st.empty()) {
                int u = st.back();
                st.pop_back();
                tin[u] = ++timer;
                rev[timer] = u;
                for (auto e : g[u])
                    if (e.first != up[0][u]) {
                        int v = e.first;
                        up[0][v] = u;
                        d[v] = d[u] + 1;
                        dist[v] = dist[u] + e.second;
                        st.push_back(v);
                    }
            }
            for (int k = 1; k < L; ++k)
                for (int u = 1; u <= n; ++u)
                    up[k][u] = up[k - 1][up[k - 1][u]];
        }
        int lca(int u, int v) {
            if (d[u] < d[v])
                swap(u, v);
            int x = d[u] - d[v];
            for (int k = 0; k < L; ++k)
                if (x >> k & 1)
                    u = up[k][u];
            if (u == v)
                return u;
            for (int k = L - 1; k >= 0; --k)
                if (up[k][u] != up[k][v])
                    u = up[k][u], v = up[k][v];
            return up[0][u];
        }
        ll dis(int u, int v) {
            return dist[u] + dist[v] - 2 * dist[lca(u, v)];
        }
    };
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        Tree t(n);
        for (int i = 1, u, v, w; i < n; ++i) {
            cin >> u >> v >> w;
            t.add(u, v, w);
        }
        t.build();
        while (m--) {
            int u, v;
            cin >> u >> v;
            cout << t.dis(u, v) << '\n';
        }
    }
    
    • 1

    信息

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