1 条题解

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

    思路与算法

    任意固定一条直径可能漏掉其它同长度路径上的点,因此需要判断每个点是否属于至少一条直径。

    以0为根,迭代得到父亲和遍历顺序,再逆序求每点来自孩子方向的最长、次长分支best、second。所有best[u]+second[u]的最大值就是直径长度D。

    正序传递父侧分支up:对u的孩子v,先从u的孩子分支中排除v,再与up[u]比较取较大者,加1得到up[v]。排除时,若v提供了best[u],改用second[u];否则仍用best[u]。

    对每点,把父侧up与两条孩子分支放在一起取前两大值,若其和等于D,就输出该点。按编号0..n-1检查,自然得到升序答案。代码用边数度量长度,比题面路径点数少1,不改变最长路径的集合。

    正确性说明

    逆序处理保证每个孩子向下最远距离已知,因此best和second准确表示两条不同孩子方向的最长分支。任意树路径在其最高点处分成至多两条向下分支,故全树这些和的最大值是D。

    从v经过父亲u的路径,除去边v-u后只能走u的父侧或其它孩子分支;排除v后取最大值并加1,恰得到v的父侧最远距离。归纳可知全部up值正确。

    任何经过u的简单路径最多使用u两侧不同方向的分支,前两大分支之和正是经过u的最长路径。它等于D当且仅当存在一条全树最长路径经过u,因此逐点判定得到所有直径顶点的并集。

    复杂度

    n点。邻接表与全部数组的初始化O(n),正逆遍历、父侧传递及输出判定均只访问每条边常数次,总时间O(n),空间O(n)。遍历采用显式顺序数组,不依赖深递归。

    易错点

    最长分支有并列时,次长也可能等于最长,必须保留两条不同孩子方向的贡献。排除当前孩子时不能直接使用包含它的最大值。单点树的直径长度为0,唯一节点0也应输出。题目只要节点编号列表,不要额外输出直径长度或答案个数。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<vector<int>> g(n);
        for (int i = 1, u, v; i < n; ++i) {
            cin >> u >> v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        vector<int> parent(n, -1), order(1, 0), best(n), second(n), up(n);
        parent[0] = 0;
        for (int i = 0; i < n; ++i)
            for (int v : g[order[i]])
                if (v != parent[order[i]])
                    parent[v] = order[i], order.push_back(v);
        int diameter = 0;
        for (int i = n - 1; i >= 0; --i) {
            int u = order[i];
            for (int v : g[u])
                if (parent[v] == u) {
                    int d = best[v] + 1;
                    if (d > best[u])
                        second[u] = best[u], best[u] = d;
                    else if (d > second[u])
                        second[u] = d;
                }
            diameter = max(diameter, best[u] + second[u]);
        }
        for (int u : order)
            for (int v : g[u])
                if (parent[v] == u) {
                    int other = best[v] + 1 == best[u] ? second[u] : best[u];
                    up[v] = max(up[u], other) + 1;
                }
        for (int u = 0; u < n; ++u) {
            int a = best[u], b = second[u];
            if (up[u] > a)
                b = a, a = up[u];
            else
                b = max(b, up[u]);
            if (a + b == diameter)
                cout << u << '\n';
        }
    }
    
    • 1

    信息

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