1 条题解

  • 0
    @ 2026-10-26 20:16:52

    思路与算法

    先求每个有向邻接分支的内部广播耗时。已知各分支耗时d,按d降序逐次打电话,总时间max(d_i+i+1)。后序求孩子侧,再前序换根;排序后用前缀max和后缀max排除某个邻居,并将其后的电话次序提前1,得到传向该邻居的父侧耗时。各根答案最后加外部告知的1时间。

    正确性说明

    两相邻待通知分支若较短者先于较长者,交换后较长者提前、较短者延后,其最大完成时间不会增加,所以降序最优。子树收到消息后的传播彼此独立,由该局部最优归纳。换根完整收集每个邻居方向信息,排除当前方向后准确计算反向分支,故所有起点答案均为最优。

    复杂度

    各节点邻居排序总O(nlogn),后序、前后缀和初始化O(n),时间O(nlogn),空间O(n)。

    易错点

    题目最开始把消息告诉首人也耗时1。前后缀排除后,后面的名次要减1;不能简单取最大孩子值。单人答案1,第二行列出全部最优起点,按编号升序。

    参考代码(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);
        vector<int> par(n, -1), ord(1, 0), down(n), up(n), ans(n);
        for (int i = 1, p; i < n; i++) {
            cin >> p;
            --p;
            g[p].push_back(i);
            g[i].push_back(p);
        }
        for (int i = 0; i < n; i++)
            for (int v : g[ord[i]])
                if (v != par[ord[i]]) {
                    par[v] = ord[i];
                    ord.push_back(v);
                }
        for (int z = n - 1; z >= 0; z--) {
            int u = ord[z];
            vector<int> a;
            for (int v : g[u])
                if (v != par[u])
                    a.push_back(down[v]);
            sort(a.rbegin(), a.rend());
            for (int i = 0; i < (int)a.size(); i++)
                down[u] = max(down[u], a[i] + i + 1);
        }
        for (int u : ord) {
            vector<pair<int, int>> a;
            for (int v : g[u])
                a.push_back({v == par[u] ? up[u] : down[v], v});
            sort(a.rbegin(), a.rend());
            int d = a.size();
            vector<int> pre(d + 1), suf(d + 1);
            for (int i = 0; i < d; i++)
                pre[i + 1] = max(pre[i], a[i].first + i + 1);
            for (int i = d - 1; i >= 0; i--)
                suf[i] = max(suf[i + 1], a[i].first + i);
            ans[u] = 1 + pre[d];
            for (int i = 0; i < d; i++)
                if (a[i].second != par[u])
                    up[a[i].second] = max(pre[i], suf[i + 1]);
        }
        int best = *min_element(ans.begin(), ans.end());
        cout << best << '\n';
        for (int i = 0; i < n; i++)
            if (ans[i] == best)
                cout << i + 1 << ' ';
        cout << '\n';
    }
    
    • 1

    信息

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