NC16527 [NOIP2013]货车运输
题目
题目描述
A 国有 n 座城市,编号从 1 到 n ,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入描述
第一行有两个用一个空格隔开的整数 n,m ,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x, y, z ,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意: x 不等于 y ,两座城市之间可能有多条道路 。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出描述
共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出 -1 。
示例1
输入
4 3 1 2 4 2 3 3 3 1 1 3 1 3 1 4 1 3
输出
3 -1 3
备注
对于 30% 的数据, 0 < n < 1,000,0 < m < 10,000,0 < q< 1,000 ;
对于 60% 的数据, 0 < n < 1,000,0 < m < 50,000,0 < q< 1,000 ;
对于 100% 的数据, 0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000 。
题解
知识点:倍增,LCA,并查集,最小生成树。
显然,若两点有多条路径,我们一定选择一条最大生成树上的路径,这样保证走的每条边都是权值最大的,因此我们先求出最大生成树,代替原图。
然后就是查询任意两点路径的最小边,我们可以使用树剖,但是这里问题是静态的,因此用树上倍增会更好写。
注意,每次查询之前先判定两点是否连通,这个可以用并查集实现。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; struct DSU { vector<int> fa; DSU(int n = 0) { init(n); } void init(int n) { fa.assign(n + 1, 0); iota(fa.begin(), fa.end(), 0); } int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } bool same(int x, int y) { return find(x) == find(y); } void merge(int x, int y) { fa[find(x)] = find(y); } }; template<class T> struct Graph { struct edge { int v, nxt; T w; }; int idx; vector<int> h; vector<edge> e; Graph(int n = 0, int m = 0) { init(n, m); } void init(int n, int m) { idx = 0; h.assign(n + 1, 0); e.assign(m + 1, {}); } void add(int u, int v, T w) { e[++idx] = { v,h[u],w }; h[u] = idx; } }; const int N = 10007, M = 50007; Graph<int> g; struct node { int u, v, w; }e[M]; bool vis[N]; int f[27][N], mi[27][N], dep[N]; void dfs(int u, int fa, int faw) { vis[u] = 1; f[0][u] = fa; mi[0][u] = faw; dep[u] = dep[fa] + 1; for (int i = 1;i <= 20;i++) { f[i][u] = f[i - 1][f[i - 1][u]]; mi[i][u] = min(mi[i - 1][u], mi[i - 1][f[i - 1][u]]); } for (int i = g.h[u];i;i = g.e[i].nxt) { int v = g.e[i].v, w = g.e[i].w; if (vis[v]) continue; dfs(v, u, w); } } int get_ans(int u, int v) { int ans = 1e9; if (dep[u] < dep[v]) swap(u, v); for (int i = 20;i >= 0;i--) { if (dep[f[i][u]] >= dep[v]) { ans = min(ans, mi[i][u]); u = f[i][u]; } if (u == v) return ans; } for (int i = 20;i >= 0;i--) { if (f[i][u] != f[i][v]) { ans = min({ ans, mi[i][u], mi[i][v] }); u = f[i][u]; v = f[i][v]; } } return min({ ans, mi[0][u], mi[0][v] }); } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; for (int i = 1;i <= m;i++) { int u, v, w; cin >> u >> v >> w; e[i] = { u,v,w }; } sort(e + 1, e + m + 1, [&](const node &a, const node &b) {return a.w > b.w;}); DSU dsu(n); g.init(n, n << 1); for (int i = 1;i <= m;i++) { auto [u, v, w] = e[i]; if (dsu.same(u, v)) continue; dsu.merge(u, v); g.add(u, v, w); g.add(v, u, w); } for (int i = 1;i <= n;i++) if (!vis[i]) dfs(i, 0, 0); int q; cin >> q; while (q--) { int u, v; cin >> u >> v; if (!dsu.same(u, v)) cout << -1 << '\n'; else cout << get_ans(u, v) << '\n'; } return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17498902.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
2022-06-23 NC50038 kotori和糖果
2022-06-23 Contest
2022-06-23 NC19115 选择颜色
2022-06-23 NC23046 华华教月月做数学
2022-06-23 NC14731 逆序对
2022-06-23 NC15052 求最值
2022-06-23 NC50999 表达式计算4