题解-luogu-p5021赛道修建

题目链接

给定一个有$n$个节点的树,在其中选出$m$条没有公共边的路径,并使得$m$条路径中最短路径的长度尽可能大。输出这个最短路径的长度。

$2\le n \le 50000,1\le m\le n-1 $

题目要求使$m$条赛道中最短赛道的长度尽可能大,不难想到二分最短赛道的长度$len$,并判定是否能修建出$m$条赛道。

定义$f[u]$为自节点$u$向下延伸的不作为赛道的最长链长度。假设已知所有的$f[v]+w(u,v) (v\in son(u))$(即自节点$u$向下延伸的所有链的长度),则我们应在保证这些链能组成最多赛道的前提下,使保留下来的$f[v]+w(u,v)$最大。

考虑贪心。对于$f[v]+w(u,v)\ge len$的情况,可以直接将其作为一条赛道。而剩余的链,只能将它们两两拼接成赛道。由于需要使保留下来的$f[v]+w(u,v)$取最大值,所以我们可以优先使较短的链得到匹配,在剩余的无法匹配的链中取最值作为新的$f[u]$。

贪心操作可以用$multiset$实现。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 50005;
struct Edge {int v, w;};
vector<Edge> G[MAXN];
int N, M, L, cnt;
int f[MAXN];
inline bool cmp(int a, int b) {return a > b;}
inline void dfs(int u, int fa) {
multiset<int> s;
for (vector<Edge>::iterator it = G[u].begin(); it != G[u].end(); it++) {
int v = it -> v, w = it -> w;
if (v == fa) continue;
dfs(v, u);
if (f[v] + w >= L) cnt++;
else s.insert(f[v] + w);
}
while (!s.empty()) {
multiset<int>::iterator it = s.begin();
s.erase(it);
multiset<int>::iterator it1 = s.lower_bound(L - *it);
if (it1 == s.end())
f[u] = max(f[u], *it);
else {
cnt++;
s.erase(it1);
}
}
}
inline bool check() {
memset(f, 0, sizeof(f));
cnt = 0;
dfs(1, 0);
if (cnt >= M) return 1;
return 0;
}
int main() {
scanf("%d%d", &N, &M);
for (register int i = 1; i < N; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
G[u].push_back((Edge){v, w});
G[v].push_back((Edge){u, w});
}
int l = 0, r = 500000000;
int ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
L = mid;
if (check()) {
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
printf("%d", ans);
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×