luogu4234 最小差值生成樹
題目大意
在一個帶權無向圖中,它的最小差值生成樹為最大邊與最小邊差值最小的生成樹。求一個圖的最小差值生成樹。
題解
30分解法
引理1 最小生成樹的最大邊的邊權是所有生成樹中最大邊邊權中的最小值。
證明:任意一棵生成樹都可以在最小生成樹的基礎上,通過不斷取一個樹外邊e,將其替換掉其與生成樹所在環中的一條邊的方式而得到。我們就看看第一條用來替換的邊的情況吧。在不在最小生成樹中的邊中任取一個邊權小於最小生成樹最大邊m的邊e,則e必然與最小生成樹的樹邊形成環。若m不在環中,那麽就是替換掉任意一條邊,答案也沒有影響。如果m在環中,且用e替換掉m可以得到一個最大邊權更小的生成樹,那麽原來的最小生成樹就不是最小生成樹了。因此原命題成立。
因此,我們可以將邊排序,不斷將最小邊刪除並求一遍Kruskal,最終取min即可。
100分解法
拆邊,用LCT。先將最小生成樹加入LCT中,然後從小到達枚舉每一條樹外邊,將其和樹邊所在環中最小邊刪除然後納入LCT中,每次在外部更新最大值與最小值的差的最小值即可。
解法正確性證明
證明目標 若答案生成樹的最小邊權和最大邊權為L‘, R‘,則當我們按照此方法枚舉到R‘時,L‘就是當前生成樹中的最小邊權。
假設在經過R‘之前,中間狀態生成樹的最小邊權為L(L < L‘),最大邊權為R。
引理2 邊權位於[L, R‘]內的邊集中必然存在一條邊e,使得e和邊權為L的邊位於一個環內,且L為最小邊權。
證明:假設命題不成立,如果要使答案為L‘,邊權L的邊必須去除。如果[L, R‘]內沒有滿足條件的e,則e的邊權>R‘,這與R‘的定義矛盾。
引理3 在中間狀態下,若邊權為L‘的邊在生成樹內,則邊權位於[L, R‘]內的邊集中必然不存在一條邊e,e和邊權為L‘的邊在一個環內,且L‘是環中的最小邊權。
證明:假設命題不成立,L‘不在生成樹內,答案就不可能是[L‘, R‘]。
引理4 在中間狀態下,若邊權為L‘的邊不在生成樹內,則在[L, L‘]中必然存在一條邊e,使得邊權為L‘的邊和e在一個環內,且L‘不是環中的最小邊。
證明:假設命題不成立,那麽在L‘所在環中選其它一條邊,邊權為L‘‘,則L‘‘, R是一個更優的答案,產生了矛盾。
#include <cstdio> #include <cstring> #include <algorithm> #include <cassert> using namespace std; const int MAX_NODE = 50010, MAX_EDGE = 200010, INF = 0x3f3f3f3f; int TotNode, TotEdge, Ans; struct Edge { int From, To, Weight; bool InTree; bool operator < (const Edge& a) const { return Weight < a.Weight; } }_edges[MAX_EDGE]; int MaxEdgeP, MinEdgeP; struct LCT { private: static const int MAX_TREE_NODE = MAX_NODE + MAX_EDGE; struct Node { int Val, Id; bool Rev; Node *Father, *LeftSon, *RightSon, *MinValP; bool IsRoot() { return !Father || (Father->LeftSon != this && Father->RightSon != this); } bool IsLeftSon() { return Father->LeftSon == this; } void Refresh() { MinValP = this; if (LeftSon && LeftSon->MinValP->Val < MinValP->Val) MinValP = LeftSon->MinValP; if (RightSon && RightSon->MinValP->Val < MinValP->Val) MinValP = RightSon->MinValP; } void Reverse() { swap(LeftSon, RightSon); Rev = !Rev; } void PushDown() { if (Rev) { if (LeftSon) LeftSon->Reverse(); if (RightSon) RightSon->Reverse(); Rev = false; } } }_nodes[MAX_TREE_NODE]; struct SplayTree { private: Node *InnerRoot; void PushDown(Node *cur) { if (!cur->IsRoot()) { PushDown(cur->Father); } cur->PushDown(); } void Rotate(Node *cur) { Node *gfa = cur->Father->Father; Node **gfaSon = cur->Father->IsRoot() ? &InnerRoot : cur->Father->IsLeftSon() ? &gfa->LeftSon : &gfa->RightSon; Node **faSon = cur->IsLeftSon() ? &cur->Father->LeftSon : &cur->Father->RightSon; Node **curSon = cur->IsLeftSon() ? &cur->RightSon : &cur->LeftSon; *faSon = *curSon; if (*faSon) (*faSon)->Father = cur->Father; *curSon = cur->Father; (*curSon)->Father = cur; *gfaSon = cur; (*gfaSon)->Father = gfa; (*curSon)->Refresh(); cur->Refresh(); } public: void Splay(Node *cur) { PushDown(cur); while (!cur->IsRoot()) { if (!cur->Father->IsRoot()) Rotate(cur->Father->IsLeftSon() == cur->IsLeftSon() ? cur->Father : cur); Rotate(cur); } } }t; void Access(Node *cur) { Node *prev = NULL; while (cur) { t.Splay(cur); cur->RightSon = prev; cur->Refresh(); prev = cur; cur = cur->Father; } } void MakeRoot(Node *cur) { Access(cur); t.Splay(cur); cur->Reverse(); } void MakePath(Node *u, Node *v) { MakeRoot(v); Access(u); t.Splay(u); } void Link(Node *u, Node *v) { MakeRoot(v); v->Father = u; } void Cut(Node *u, Node *v) { MakePath(u, v); assert(v->Father == u); assert(u->LeftSon == v); u->LeftSon = NULL; v->Father = NULL; u->Refresh(); } Node *FindRoot(Node *cur) { while (cur->Father) cur = cur->Father; while (cur->LeftSon) cur = cur->LeftSon; return cur; } Node *GetMinNode(Node *u, Node *v) { if (FindRoot(u) != FindRoot(v)) return NULL; MakePath(u, v); return u->MinValP; } public: LCT() { for (int i = 1; i <= 100; i++) _nodes[i].Id = i; } void SetNode(int v, int val) { _nodes[v].Val = val; } void Link(int u, int v) { Link(_nodes + u, _nodes + v); } void Cut(int u, int v) { Cut(_nodes + u, _nodes + v); } int GetMinId(int u, int v) { Node *ans = GetMinNode(_nodes + u, _nodes + v); if (ans == NULL) return -1; else return ans - _nodes; } }g; void InitBuild() { sort(_edges + 1, _edges + TotEdge + 1); for (int i = 1; i <= TotEdge; i++) g.SetNode(TotNode + i, _edges[i].Weight); for (int i = 1; i <= TotNode; i++) g.SetNode(i, INF); int cnt = 0, curEdge = 0; while (cnt < TotNode - 1) { curEdge++; int k = g.GetMinId(_edges[curEdge].From, _edges[curEdge].To); if (k == -1) { cnt++; g.Link(_edges[curEdge].To, curEdge + TotNode); g.Link(curEdge + TotNode, _edges[curEdge].From); _edges[curEdge].InTree = true; MaxEdgeP = curEdge; } } Ans = _edges[MaxEdgeP].Weight - _edges[1].Weight; MinEdgeP = 1; } int GetAns() { for (int i = 1; i <= TotEdge; i++) { if (_edges[i].InTree) continue; _edges[i].InTree = true; if (_edges[i].Weight > _edges[MaxEdgeP].Weight) MaxEdgeP = i; int cutEdge = g.GetMinId(_edges[i].From, _edges[i].To); assert(cutEdge != -1); g.Cut(_edges[cutEdge - TotNode].To, cutEdge); g.Cut(cutEdge, _edges[cutEdge - TotNode].From); _edges[cutEdge - TotNode].InTree = false; if (MaxEdgeP == cutEdge - TotNode) while (!_edges[MaxEdgeP].InTree) MaxEdgeP--; if (MinEdgeP == cutEdge - TotNode) while (!_edges[MinEdgeP].InTree) MinEdgeP++; Ans = min(Ans, _edges[MaxEdgeP].Weight - _edges[MinEdgeP].Weight); g.Link(_edges[i].To, i + TotNode); g.Link(i + TotNode, _edges[i].From); } return Ans; } int main() { scanf("%d%d", &TotNode, &TotEdge); for (int i = 1; i <= TotEdge; i++) { scanf("%d%d%d", &_edges[i].From, &_edges[i].To, &_edges[i].Weight); while (_edges[i].From == _edges[i].To) { TotEdge--; scanf("%d%d%d", &_edges[i].From, &_edges[i].To, &_edges[i].Weight); } } InitBuild(); printf("%d\n", GetAns()); return 0; }
luogu4234 最小差值生成樹