洛谷 P1967
阿新 • • 發佈:2020-08-25
題目連結:P1967 貨車運輸
題目大意
開局一張圖裝備全靠打
題目給了你一張圖,然後呢, 讓你找兩點之間的路徑,使路徑上的最小值最大.(大概就是這個意思)
solution
思路
最小值最大? 二分? 複雜度不對啊
Floyd? 複雜度更不對了!
那我們怎麼想呢? 貪心? 對, 就是貪心!
我們建一顆最大生成樹,也就是最大瓶頸生成樹(如果會生成樹,這個地方就明白了為什麼建最大生成樹是對的), 樹上兩點間的路徑的最小值一定是最大值
那建完樹之後,我們怎麼做呢?樹剖?沒必要,太浪費了,畢竟連修改都木有
我們簡單的倍增一下就可以了,樹上兩點之間路徑是唯一的,所以我們可以 \(LCA\) ,記錄一下,樹上倍增求最小值, 在處理 \(LCA\)
細節
資料非常的毒瘤啊, 有時候圖還不是一張連通圖
那我們怎麼辦? 其實很簡單,我們在建樹的時候,用並查集存過了, 所以我們在求 \(LCA\) 的時候求一下兩點在沒在同一個集合裡就行了
code:
/** * Author: Alieme * Data: 2020.8.25 * Problem: Luogu P1967 * Time: O(nlogn) */ #include <cstdio> #include <iostream> #include <string> #include <cstring> #include <cmath> #include <algorithm> #define int long long #define rr register #define inf 1e9 #define MAXN 100010 using namespace std; inline int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= ch == '-', ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } void print(int x) { if (x < 0) putchar('-'), x = -x; if (x > 9) print(x / 10); putchar(x % 10 + 48); } struct Gragh { int u; int v; int w; Gragh() {} Gragh(int U, int V, int W) { u = U, v = V, w = W;} bool operator < (const Gragh &b) const{return w > b.w;} // 貪心 }a[MAXN]; struct Edge { int nxt; int to; int val; Edge() {} Edge(int Nxt, int To, int Val) {nxt = Nxt, to = To, val = Val;} }e[MAXN]; int n, m, q, tot; int fath[MAXN], head[MAXN], dep[MAXN]; int fa[MAXN][30], w[MAXN][30]; bool vis[MAXN]; inline void add(int from, int to, int val) { e[++tot] = Edge(head[from], to, val); head[from] = tot; } int find(int x) { // 並查集 if (x != fath[x]) fath[x] = find(fath[x]); return fath[x]; } inline void Union(int x, int y) { fath[find(x)] = find(y);} inline void klus() { // klus建最大生成樹 sort(a + 1, a + 1 + m); for (rr int i = 1; i <= n; i++) fath[i] = i; for (rr int i = 1; i <= m; i++) if (find(a[i].u) != find(a[i].v)) { Union(a[i].u, a[i].v); add(a[i].u, a[i].v, a[i].w); add(a[i].v, a[i].u, a[i].w); } } void dfs(int x) { // LCA的dfs vis[x] = 1; for (rr int i = head[x]; i; i = e[i].nxt) { int to = e[i].to; if (vis[to]) continue; dep[to] = dep[x] + 1; fa[to][0] = x; w[to][0] = e[i].val; dfs(to); } } inline int LCA(int u, int v) { // 樹上倍增找最小值 if (find(u) != find(v)) return -1; // 兩點不聯通 int ans = inf; if (dep[u] > dep[v]) swap(u, v); for (rr int i = 20; i >= 0; i--) if (dep[fa[v][i]] >= dep[u]) { ans = min(ans, w[v][i]); v = fa[v][i]; } if (u == v) return ans; for (rr int i = 20; i >= 0; i--) { if (fa[u][i] != fa[v][i]) { ans = min(ans, min(w[u][i], w[v][i])); u = fa[u][i]; v = fa[v][i]; } } ans = min(ans, min(w[u][0], w[v][0])); return ans; } signed main() { n = read(); m = read(); for (rr int i = 1; i <= m; i++) { int u = read(); int v = read(); int w = read(); a[i] = Gragh(u, v, w); } klus(); // LCA初始化 for (rr int i = 1; i <= n; i++) if (!vis[i]) { dep[i] = 1; dfs(i); fa[i][0] = i; w[i][0] = inf; } for (rr int i = 1; i <= 20; i++) for (rr int j = 1; j <= n; j++) { fa[j][i] = fa[fa[j][i - 1]][i - 1]; w[j][i] = min(w[j][i - 1], w[fa[j][i - 1]][i - 1]); } q = read(); while (q--) { int x = read(); int y = read(); print(LCA(x, y)); puts(""); } }