P5021 [NOIP2018 提高组] 赛道修建 (二分+树上贪心)
upd 2024.11.14
求最大值最小,通常先从二分思考。
枚举完二分的值以后,也就限制了最小值的下界。我们要让满足下界的赛道尽可能多。
由于是树,解决这样的问题就有树上 dp 和贪心。
到底是哪一个?走 dp 的话,状态已经不好设计了,难以描述一个局面。
所以走贪心。树上贪心通常自底向上贪,因为每个子树最多向上给一条链,所以让子树内能拼的都拼完一定是最优的,不会等到上面再合并。拼完后就往上传剩下里面最优的。
怎么实现?直接用多重集维护当前链集是常见的手段。
在树上选
看到最小值最大,很自然想到二分模型:枚举最小值
如何在树上选出
普遍的思路是从叶子节点按子树合并,我们要让该子树对父亲的贡献最大,一定是在该子树已经无法拼出路径(贪心)后剩下的路径中取最大值(由于父亲唯一,所以只能留下最大值)。
设
复杂度为
总结:二分模型,树形贪心的基本思路,合适的数据结构维护。
#include <bits/stdc++.h>
typedef long long ll;
int read() {
int x = 0, f = 1;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') f = -1;
c = getchar();
}
while(isdigit(c)) {
x = (x << 3) + (x << 1) + (c - '0');
c = getchar();
}
return x * f;
}
int n, m, cnt, L, ret, ans;
int h[50010], f[50010];
struct node {
int to, nxt, w;
} e[100010];
void add(int u, int v, int w) {
e[++cnt].to = v;
e[cnt].nxt = h[u];
e[cnt].w = w;
h[u] = cnt;
}
void dfs(int u, int fa) {
std::multiset<int> s;
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
dfs(v, u);
if(f[v] + e[i].w >= L) ret++;
else s.insert(f[v] + e[i].w);
}
while(!s.empty()) {
std::multiset<int>::iterator it = s.begin();
s.erase(it);
std::multiset<int>::iterator it2 = s.lower_bound(L - *it);
if(it2 == s.end()) {
f[u] = std::max(f[u], *it);
}
else {
s.erase(it2);
ret++;
}
}
}
bool check(int x) {
L = x;
ret = 0;
memset(f, 0, sizeof(f));
dfs(1, 0);
return ret >= m;
}
void Solve() {
n = read(), m = read();
int r = 0;
for(int i = 1; i < n; i++) {
int u = read(), v = read(), w = read();
add(u, v, w), add(v, u, w);
r += w;
}
int l = 0;
r = r / m;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid)) l = mid + 1, ans = mid;
else r = mid - 1;
}
std::cout << ans << "\n";
}
int main() {
Solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具