题解: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;
}

浙公网安备 33010602011771号