Luogu P1967 貨車運輸
阿新 • • 發佈:2020-07-26
思路
這道題確實有含金量,值得一做。先說一下我的做題過程。
這個題本來第一眼是想用Prim+樹剖LCA來做的,但是發現如果用Prim跑最大生成樹的話做重構樹會極其困難。捨棄。
然後想用Kruskal+樹剖LCA做。但是我又悲催地發現用樹剖LCA難以統計邊權的最小值(雖然快啊啊啊)。捨棄。
最後才是Kruskal+倍增LCA。
這個題表面上看好像就是把最大生成樹的板子和LCA的板子套在了一起,但是事實遠沒有看起來那麼簡單。
這個題的難點並不在於最大生成樹和重構樹上,而是在於如何高效地找出兩點間路徑上的權值的最小值(其實這個可以用樹鏈剖分套線段樹來做,但是我不會)。顯然如果你先通過某種方法求出了LCA,再
去遍歷求路徑上的最小值是不現實的。那麼我們就要考慮在求LCA的過程中求出兩點間路徑的最小值。
我們考慮維護一個數組w,w[i][j]表示從i向上跳j步經過的路徑的權值最小是什麼。這個東西和f陣列的維護方法大同小異,沒啥特別的(這是對於倍增LCA來說的)。其他的部分就套個板子就行了。
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #define MAXN 10050 #define MAXM 50050 #define INF 0x7fffffff #define DEBUG puts("OK"); int n, m, q; int fa[MAXN]; int head[MAXN], cnt; int dep[MAXN], f[MAXN][23]; int w[MAXN][23]; bool vis[MAXN]; struct node_graph{ int from, to; int val; } graph[MAXM]; struct node_tree{ int nxt, to; int val; } tree[MAXN << 1]; inline int read(void){ int f = 1, x = 0;char ch; do{ch = getchar();if(ch=='-')f = -1;} while (ch < '0' || ch > '9'); do{ x = x * 10 + ch - '0';ch = getchar();} while (ch >= '0' && ch <= '9'); return f * x; } inline void add_edge_tree(int x,int y,int z){ ++cnt; tree[cnt].nxt = head[x]; tree[cnt].to = y; tree[cnt].val = z; head[x] = cnt; return; } inline int _min(int x,int y){ return x < y ? x : y; } inline void _swap(int &x,int &y){ int tmp = x; x = y, y = tmp; return; } inline bool cmp(const node_graph a,const node_graph b){ return a.val > b.val; } inline int get_father(int x){ return fa[x] == x ? x : fa[x] = get_father(fa[x]); } void Kruskal(void){ int tot = 0; std::sort(graph + 1, graph + m + 1, cmp); for (int i = 1; i <= n;++i) fa[i] = i; for (int i = 1; i <= m;++i){ int u = get_father(graph[i].from), v = get_father(graph[i].to); if(u==v) continue; fa[u] = v, ++tot; add_edge_tree(u, v, graph[i].val); add_edge_tree(v, u, graph[i].val); if(tot==n-1) break; } return; } void DFS(int k){ vis[k] = 1; for (int i = head[k]; i; i = tree[i].nxt){ int v = tree[i].to; if(vis[v]) continue; dep[v] = dep[k] + 1; f[v][0] = k; w[v][0] = tree[i].val; DFS(v); } return; } inline void _init(void){ for (int i = 1; i <= n;++i){ if(!vis[i]){ dep[i] = 1;DFS(i); f[i][0] = i, w[i][0] = INF; } } for (int i = 1; i <= 21;++i){ for (int j = 1; j <= n;++j){ f[j][i] = f[f[j][i - 1]][i - 1]; w[j][i] = _min(w[j][i - 1], w[f[j][i - 1]][i - 1]); } } return; } inline int LCA(int x,int y){ if(get_father(x)!=get_father(y)) return -1; int res = INF; if(dep[x]>dep[y]) _swap(x, y); for (int i = 20; i >= 0;--i){ if(dep[f[y][i]]>=dep[x]){ res = _min(res, w[y][i]); y = f[y][i]; } } if(x==y) return res; for (int i = 21; i >= 0;--i){ if(f[x][i]!=f[y][i]){ res = _min(res, _min(w[y][i], w[x][i])); x = f[x][i], y = f[y][i]; } } res = _min(res, _min(w[x][0], w[y][0])); return res; } int main(){ n = read(), m = read(); for (int i = 1; i <= m;++i) graph[i].from = read(), graph[i].to = read(), graph[i].val = read(); Kruskal(); _init(); q = read(); for (int i = 1; i <= q;++i){ int x = read(), y = read(); printf("%d\n", LCA(x, y)); } return 0; }