1. 程式人生 > >luogu1967 貨車運輸 最大瓶頸生成樹

luogu1967 貨車運輸 最大瓶頸生成樹

The kruskal 無向圖 復雜 turn div print ring des

題目大意

給出一張圖,給出q對點,求這兩個點間權值最小邊最大的路徑,輸出這個最小邊權。

題解

我們先一條一條邊建圖。當建立的邊使得圖中形成環時,因為環中的每個節點只考慮是否連通和瓶頸大小,要想互相連通只要一條路就夠了,而只有環上的最小邊和次小邊可能是這條路的瓶頸,且這條路的瓶頸肯定越大越好。故根據貪心,我們可以直接把環中的權值最小邊刪去。
所以我們就維護一個LCT來隨時刪邊增邊,還要用到拆邊等方法來統計路徑上的值嗎?能AC,但太復雜了!
我們從整體考慮,第一段敘述中,每次遇到一個環,其值為S。由於去掉的是最小邊,邊權w,所以剩余的路徑上的邊權和S-w是最大的。所以這就是一個最大生成樹。所以我們就用Kruskal算法求出最大生成樹,再由樹上倍增求解即可。註意Kruskal處理的是單向邊而不是無向圖,所以先Kruskal,再建圖。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdarg>
using namespace std;

void _printf(char *format, ...) {
#ifdef _DEBUG
    va_list(args);
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
#endif
}
const int MAX_NODE = 10010, MAX_EDGE = 50010
* 2, MAX_LOG = 20, INF = 0x3f3f3f3f; struct Node; struct Edge; struct Node { Edge *Head; Node *Elder[MAX_LOG]; Node *Root; int MinVal[MAX_LOG]; int Depth; Node *Father; }_nodes[MAX_NODE]; int _vCount; struct Edge { Node *From, *To; Edge *Next; int Weight; Edge(Node *from, Node *to, int
w):From(from),To(to),Weight(w),Next(NULL){} Edge() {} }_edges[MAX_NODE * 2], tempEdges[MAX_EDGE]; int _eCount, tempeCount; Edge *NewEdge() { return _edges + (++_eCount); } Edge *AddEdge(Node *from, Node *to, int w) { Edge *e = NewEdge(); e->To = to; e->From = from; e->Weight = w; e->Next = from->Head; from->Head = e; return e; } void Build(int uId, int vId, int w) { tempEdges[++tempeCount] = Edge(_nodes + uId, _nodes + vId, w); } int Log2(int x) { int ans = 0; while (x >>= 1) ans++; return ans; } void Dfs(Node *cur) { int topFa = Log2(cur->Depth); for (int i = 1; i <= topFa && cur->Elder[i - 1]; i++) { cur->Elder[i] = cur->Elder[i - 1]->Elder[i - 1]; cur->MinVal[i] = min(cur->MinVal[i - 1], cur->Elder[i - 1]->MinVal[i - 1]); } for (Edge *e = cur->Head; e; e = e->Next) { if (!e->To->Depth) { e->To->Depth = cur->Depth + 1; e->To->Elder[0] = cur; e->To->Root = cur->Root; e->To->MinVal[0] = e->Weight; Dfs(e->To); } } } void DfsStart() { for (int i = 1; i <= _vCount; i++) { if (!_nodes[i].Depth) { _nodes[i].Depth = 1; _nodes[i].MinVal[0] = INF; _nodes[i].Root = _nodes + i; Dfs(_nodes + i); } } } int Lca(Node *low, Node *high) { if (low->Root != high->Root) return -1; int ans = INF; if (low->Depth < high->Depth) swap(low, high); int len = low->Depth - high->Depth, stepCnt = Log2(len); for (int k = 0; k <= stepCnt; k++) { if ((1 << k)&len) { ans = min(ans, low->MinVal[k]); low = low->Elder[k]; } } if (low == high) return ans; for (int k = Log2(low->Depth); k >= 0; k--) { if (low->Elder[k] != high->Elder[k]) { ans = min(ans, low->MinVal[k]); ans = min(ans, high->MinVal[k]); low = low->Elder[k]; high = high->Elder[k]; } } ans = min(ans, low->MinVal[0]); ans = min(ans, high->MinVal[0]); return ans; } bool CmpEdge(Edge a, Edge b) { return a.Weight > b.Weight; } Node *FindFather(Node *cur) { return cur == cur->Father ? cur : cur->Father = FindFather(cur->Father); } void Join(Node *root1, Node *root2) { root1->Father = root2; } void Kruskal() { sort(tempEdges + 1, tempEdges + tempeCount + 1, CmpEdge); for (int i = 1; i <= _vCount; i++) _nodes[i].Father = _nodes + i; for (int i = 1; i <= tempeCount; i++) { Edge e = tempEdges[i]; Node *root1 = FindFather(e.From), *root2 = FindFather(e.To); if (root1 != root2) { AddEdge(e.From, e.To, e.Weight); AddEdge(e.To, e.From, e.Weight); _printf("chosen %d-%d w %d\n", e.From - _nodes, e.To - _nodes, e.Weight); Join(root1, root2); } } } int main() { int totEdge; scanf("%d%d", &_vCount, &totEdge); for (int i = 1; i <= totEdge; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); Build(u, v, w); } Kruskal(); DfsStart(); int queryCnt; scanf("%d", &queryCnt); while (queryCnt--) { int u, v; scanf("%d%d", &u, &v); printf("%d\n", Lca(_nodes + u, _nodes + v)); } return 0; }

luogu1967 貨車運輸 最大瓶頸生成樹