[NOIP2015 提高组] 运输计划(二分 + lca + 树上差分)
求操作后的所有路径中边权和最长的边的最小值,二分
考虑 对答案的最短时间二分,每次得到一个二分值 mid
显然,我们主要关心 路径和比 mid 大的运输路径,这里要维护树上两点间的路径,所以要用到 lca,同时可以记录每个点的距离(1 -> x)
操作只能做一次,贪心地考虑肯定是要删掉 任意一个经过所有
也就是个判定性的问题
-
一定要经过所有运输路径,如果删去的不是经过所有的,则肯定还有运输路径的时间要 > mid
-
删去边的边权大于等于最大差值即可,剩下的显然都会被覆盖,也就是不会超过 mid
怎么知道这个边经过了几条路径呢?
那么要对路径上所有的边 +1,记录覆盖,很容易想到用 树上差分 维护即可。
时间复杂度为
做这题还有一个小心得(也不算吧,
在实现树上差分的还原时,可以选择再 dfs 一遍,也可以在 lca 初始化树的时候开一个数组记录搜索序,后边倒序循环一遍求前缀和。
我觉得以后还是用 后者更好,因为我老是把两次的 dfs 函数名打混,时不时就调 n 年 ...
#include <bits/stdc++.h> #define re register int #define max(x, y) (x > y ? x : y) using namespace std; const int N = 3e5 + 10, logN = 50; struct edge { int to, w, next; }e[N << 1]; struct road { int x, y, p; }a[N]; int top, h[N]; int dep[N], dist[N], f[N][logN], d[N]; int n, m; int seq[N], idx; inline void add(int x, int y, int w) { e[++ top] = (edge){y, w, h[x]}; h[x] = top; } void dfs(int u, int fa) { dep[u] = dep[fa] + 1; seq[++ idx] = u; f[u][0] = fa; for (re i = 1; i <= log2(n); i ++) f[u][i] = f[f[u][i - 1]][i - 1]; for (re i = h[u]; i; i = e[i].next) { int v = e[i].to, w = e[i].w; if (v == fa) continue; dist[v] = dist[u] + w; dfs(v, u); } } inline int lca(int u, int v) { if (dep[u] < dep[v]) swap(u, v); for (re i= log2(n); i >= 0; i --) if (dep[f[u][i]] >= dep[v]) u = f[u][i]; if (u == v) return u; for (re i = log2(n); i >= 0; i --) if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i]; return f[u][0]; } /* void cale(int u, int fa) { for (re i = h[u]; i; i = e[i].next) { int v = e[i].to; if (v == fa) continue; cale(v, u); d[u] += d[v]; } } */ inline bool check(int mid) { memset(d, 0, sizeof(d)); int cnt = 0, maxt = 0; for (re i = 1; i <= m; i ++) { int x = a[i].x, y = a[i].y, p = a[i].p; int t = dist[x] + dist[y] - dist[p] * 2; if (t > mid) { d[x] += 1; d[y] += 1; d[p] -= 2; cnt ++; maxt = max(maxt, t - mid); } } if (!cnt) return true; // cale(1, 0); for (re i = n; i >= 1; i --) { int x = seq[i]; d[f[x][0]] += d[x]; } for (re i = 2; i <= n; i ++) if (d[i] == cnt && dist[i] - dist[f[i][0]] >= maxt) return true; return false; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> m; for (re i = 1; i < n; i ++) { int x, y, w; cin >> x >> y >> w; add(x, y, w); add(y, x, w); } dfs(1, 0); for (re i = 1; i <= m; i ++) { int x, y; cin >> x >> y; a[i] = (road){x, y, lca(x, y)}; } int l = 0, r = 3e8; while (l < r) { int mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid + 1; } cout << l << '\n'; 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工具