题目大意
在一个带权无向图中,它的最小差值生成树为最大边与最小边差值最小的生成树。求一个图的最小差值生成树。
题解
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'为答案生成树的最大边,则[L, R']间必然存在一条边e',使得e'与e在一个环内。因为如果边a, b在一个环内,b, c也在一个环内,则a, c也在一个环内,所以e'和边权为L的边在一个环内。这样e'就是一个满足条件的边,与原假设不符。
引理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; 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: 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 ", GetAns()); return 0; }