题解:P5021 [NOIP 2018 提高组] 赛道修建

显然是二分答案,下面讲 check 部分。

从上往下做显然是不容易的。因此考虑从下往上。明确一个贪心原则:能成合法链(即长度大于 mid)就尽量匹配(即使长度合法),因为无论多长贡献都为 \(1\)

我们维护当前点的最长链值(按照贪心原则,这个值是小于 mid 的。),对于一个节点及其所有子节点的这一层,在加上边长后排序。我们尽量地多选择链匹配(这应该是从小到大匹配),并使剩余最大值继承上去。

用 multiset 维护即可,时间复杂度 \(\mathcal O(n\log^2n)\)

bool check (int len) {
    queue <int> q;
    for (int i = 1; i <= n; i ++) deg[i] = g[i].size () - (i != 1);
    for (int i = 2; i <= n; i ++) if (!deg[i]) q.push (i); 

    int cnt = 0;
    while (!q.empty ()) {
        int u = q.front (); q.pop ();
        int mx = 0;
        for (auto it = qq[u].begin (); it != qq[u].end (); ) {
            auto finder = qq[u].lower_bound (len - *it);
            // fprintf (stderr, "len = %d, %d, found %d\n", len, *t, *finder);
            bool flag = false;
            if (finder == it) ++ finder;
            if (*it >= len) {
                ++ cnt; ++ it;
                continue;
            }
            if (finder != qq[u].end ()) {
                ++ cnt;
                qq[u].erase (finder);
                flag = true;
                it = qq[u].erase (it);
            } else mx = max (mx, *it), ++ it;
        }
        if (mx + ww[u] >= len) ++ cnt;
        else  qq[ fa[u] ].insert (mx + ww[u]);

        if (!-- deg[ fa[u] ]) q.push (fa[u]);
    }

    for (int i = 1; i <= n; i ++) qq[i].clear ();

    fprintf (stderr, "len = %d, with cnt = %d\n", len, cnt);
    
    return cnt >= m;
}
posted @ 2025-09-02 23:20  Toorean  阅读(23)  评论(0)    收藏  举报