题目描述
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
输出样例#1: 复制
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。
主要思路: Kruskal重构树 + LCA
这个题的问题是求一棵树,使得两点之间的简单路径中最小边权最大
这是一个Kruskal重构树的一个基础问题,就是快速求得最小生成树上的边权最值问题。
这里是重构一棵树,像之前一样跑Kruskal,只是在每次选边时把选上的边都新建一个节点,这个节点的点权值是这个边的边权,然后把这两个点与新建节点连边,每次连边同时用并查集把新建节点一起维护。
当连完边后,并不是说直接dfs建树,而是要讨论一个问题:可能图不连通,所以这时可能是一个森林,所以每个联通块都要建树,如果不在一个联通块里,直接“-1”。
code:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <algorithm> #include <queue> #include <map> #include <vector> using namespace std; #define go(i, j, n, k) for(int i = j; i <= n; i += k) #define fo(i, j, n, k) for(int i = j; i >= n; i -= k) #define rep(i, x) for(int i = h[x]; i; i = e[i].nxt) #define mn 200010 #define inf 1 << 30 #define ll long long inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') f = -f; ch = getchar(); } while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } // 记得开2倍数组啊,因为Kruskal重构树有最多2n - 1个节点 // 此处光荣的采用树剖LCA(逃 struct kr{ int x, y, w; } ee[mn << 1]; inline bool cmp(kr a, kr b) { return a.w > b.w; } struct edge{ int v, nxt; } e[mn << 1]; int h[mn], p, w[mn]; inline void add(int a, int b) { e[++p].nxt = h[a], h[a] = p, e[p].v = b; } inline void aadd(int i, int a, int b, int c) { ee[i].x = a, ee[i].y = b, ee[i].w = c; // cout << a << " " << b << " " << c << "\n"; } // edge and node -------------------- int n, m, father[mn << 1], tot, cnt, q, vis[mn]; inline int findx(int x) { if(x == father[x]) return x; else return father[x] = findx(father[x]); } // union-find set ------------------- int fa[mn], sze[mn], top[mn], son[mn], dep[mn]; void dfs1(int x, int f, int deep) { fa[x] = f; sze[x] = 1; dep[x] = deep; vis[x] = 1; int maxson = -1; rep(i, x) { int v = e[i].v; if(v == f) continue; dfs1(v, x, deep + 1); sze[x] += sze[v]; if(sze[v] > maxson) maxson = sze[v], son[x] = v; } } void dfs2(int x, int topf) { top[x] = topf; if(!son[x]) return; dfs2(son[x], topf); rep(i, x) { int v = e[i].v; if(v == fa[x] || v == son[x]) continue; dfs2(v, v); } } inline int LCA(int x, int y) { while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x, y); x = fa[top[x]]; } return dep[x] < dep[y] ? x : y; } // LCA --------------------------------- inline void Kru() { // 基本差不多的Kruskal tot = n; go(i, 1, n, 1) father[i] = i; sort(ee + 1, ee + m + 1, cmp); go(i, 1, m, 1) { int xx = findx(ee[i].x); int yy = findx(ee[i].y); if(xx != yy) { tot++; // 毕竟是新建节点嘛QwQ father[tot] = father[xx] = father[yy] = tot; add(xx, tot); add(tot, xx); add(yy, tot); add(tot, yy); // 记得连无向边 w[tot] = ee[i].w; if(++cnt == n - 1) break; } } go(i, 1, tot, 1) { // 森林 if(!vis[i]) { int f = findx(i); dfs1(f, 0, 1), dfs2(f, f); } } } // Kruskal Refactoring Tree ---------------------- int ff[mn]; inline int findf(int x) { if(x == ff[x]) return ff[x]; else return ff[x] = findf(ff[x]); } int used[mn]; int main() { n = read(), m = read(); go(i, 1, n, 1) ff[i] = i; go(i, 1, m, 1) { int a = read(), b = read(), c = read(); aadd(i, a, b, c); int aa = findf(a), bb = findf(b); if(rand() % 2) ff[aa] = bb; // 随机合并大发吼啊 else ff[bb] = aa; } Kru(); q = read(); go(i, 1, q, 1) { int x = read(), y = read(); int xx = findx(x), yy = findx(y); if(xx != yy) { printf("-1\n"); continue; } else { printf("%d\n", w[LCA(x, y)]); } } return 0; }