题目:求一个无向图的严格次小生成树(即次小生成树的边权和严格小于最小生成树的边权和)
首先求出图中的最小生成树。任意加一条树外边都会导致环的出现。我们现在目标是在树外边集合B中,找到边b∈B,a∈b所在环,b->weight - a->weight最小且不为0。
首先,依题意,a->weight应当是环内所有边中最大或第二大(最大可能a->weight==b->weight)的。如何找呢?我们采用树上倍增的方法。定义cur->Elder[k]为cur的第k辈祖先,MaxW[k]为cur与cur->Elder[k]路径中的最长边,MaxW2[k]为cur与cur->Elder[k]路径中的严格次长边(MaxW2[k]<MaxW[k])。枚举b时,求b->From和b->To的最近公共祖先。因为求LCA的过程基础便是cur=cur->MaxW[k],于是取在此过程中MaxW与MaxW2的最大值,便可求出答案。
如何求MaxW和MaxW2?有递归式:
cur->MaxW[i] = max(cur->MaxW[i - 1], cur->Elder[i - 1]->MaxW[i - 1]); if (cur->MaxW[i - 1] == cur->Elder[i - 1]->MaxW[i - 1]) cur->MaxW2[i] = max(cur->MaxW2[i - 1], cur->Elder[i - 1]->MaxW2[i - 1]); if (cur->MaxW[i - 1] < cur->Elder[i - 1]->MaxW[i - 1]) cur->MaxW2[i] = max(cur->MaxW[i - 1], cur->Elder[i - 1]->MaxW2[i - 1]); if (cur->MaxW[i - 1] > cur->Elder[i - 1]->MaxW[i - 1]) cur->MaxW2[i] = max(cur->MaxW2[i - 1], cur->Elder[i - 1]->MaxW[i - 1]);
初值:
cur->MaxW[0] = cur->ToFa ? cur->ToFa->Weight : 0; cur->MaxW2[0] = -INF;
完整代码:

#include <cstdio> #include <cstring> #include <cassert> #include <algorithm> #include <cmath> using namespace std; #define LOOP(i,n) for(int i=1; i<=n; i++) const int MAX_NODE = 100010, MAX_EDGE = 300010 * 2, MAX_FA = 18, INF = 0x3f3f3f3f; struct Node; struct Edge; struct Node { int Id, Depth; Edge *Head, *ToFa; Node *Elder[MAX_FA], *Prev; int MaxW[MAX_FA], MaxW2[MAX_FA]; }_nodes[MAX_NODE]; Node *GRoot; int _vCount; struct Edge { Node *From, *To; Edge *Next, *Rev; int Weight; bool InTree; Edge(){} Edge(Node *from, Node *to, Edge *next, int weight) :From(from),To(to),Next(next),Weight(weight),InTree(false){} }*_edges[MAX_EDGE]; int _eCount; int Log2(int x) { int cnt = 0; while (x /= 2) cnt++; return cnt; } void Init(int vCount) { _eCount = 0; _vCount = vCount; GRoot = 1 + _nodes; memset(_nodes, 0, sizeof(_nodes)); } Edge *AddEdge(Node *from, Node *to, int w) { Edge *e = _edges[++_eCount] = new Edge(from, to, from->Head, w); from->Head = e; return e; } void Build(int uId, int vId, int w) { Node *u = uId + _nodes, *v = vId + _nodes; u->Id = uId; v->Id = vId; Edge *e1 = AddEdge(u, v, w), *e2 = AddEdge(v, u, w); e1->Rev = e2; e2->Rev = e1; } Node *GetRoot(Node *cur) { return cur->Prev ? cur->Prev = GetRoot(cur->Prev) : cur; } void Join(Node *a, Node *b) { a->Prev = b; } bool CmpEdge(Edge *a, Edge *b) { return a->Weight < b->Weight; } long long MinW; void Kruskal() { MinW = 0; sort(_edges + 1, _edges + _eCount + 1, CmpEdge); int ans = 0, cnt = 0; LOOP(i, _eCount) { if (cnt == _vCount) break; Edge *e = _edges[i]; Node *root1 = GetRoot(e->From), *root2 = GetRoot(e->To); if (root1 != root2) { cnt++; e->InTree = true; MinW += (long long)e->Weight; Join(root1, root2); } } } void Dfs(Node *cur) { cur->MaxW[0] = cur->ToFa ? cur->ToFa->Weight : 0; cur->MaxW2[0] = -INF; 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->MaxW[i] = max(cur->MaxW[i - 1], cur->Elder[i - 1]->MaxW[i - 1]); if (cur->MaxW[i - 1] == cur->Elder[i - 1]->MaxW[i - 1]) cur->MaxW2[i] = max(cur->MaxW2[i - 1], cur->Elder[i - 1]->MaxW2[i - 1]); else if (cur->MaxW[i - 1] < cur->Elder[i - 1]->MaxW[i - 1]) cur->MaxW2[i] = max(cur->MaxW[i - 1], cur->Elder[i - 1]->MaxW2[i - 1]); else if (cur->MaxW[i - 1] > cur->Elder[i - 1]->MaxW[i - 1]) cur->MaxW2[i] = max(cur->MaxW2[i - 1], cur->Elder[i - 1]->MaxW[i - 1]); } for (Edge *e = cur->Head; e; e = e->Next) { if (!e->To->Depth && (e->InTree || e->Rev->InTree)) { e->To->ToFa = e; e->To->Elder[0] = cur; e->To->Depth = cur->Depth + 1; Dfs(e->To); } } } void GetReady() { GRoot->Depth = 1; Dfs(GRoot); } void Update(int& ans, Node *a, int i, int w) { ans = max(ans, a->MaxW[i] < w ? a->MaxW[i] : a->MaxW2[i]); } int GetAltEdgeW(Edge *e) { int ans = -INF, w = e->Weight; Node *a = e->From, *b = e->To; if (a->Depth < b->Depth) swap(a, b); for (int i = Log2(a->Depth-b->Depth); i >= 0; i--) { if (a->Elder[i] && a->Elder[i]->Depth >= b->Depth) { Update(ans, a, i, w); a = a->Elder[i]; } } assert(a->Depth == b->Depth); if (a == b) return ans; for (int i = Log2(a->Depth); i >= 0; i--) { if (a->Elder[i] && a->Elder[i] != b->Elder[i]) { Update(ans, a, i, w); Update(ans, b, i, w); a = a->Elder[i]; b = b->Elder[i]; } } Update(ans, a, 0, w); Update(ans, b, 0, w); return ans; } long long Proceed() { int delta = INF; for (int i = 1; i <= _eCount; i++) if (!_edges[i]->InTree && !_edges[i]->Rev->InTree) delta = min(delta, _edges[i]->Weight - GetAltEdgeW(_edges[i])); return (long long)delta + MinW; } int main() { #ifdef _DEBUG freopen("c:\noi\source\input.txt", "r", stdin); #endif int totNode, totEdge, uId, vId, w; scanf("%d%d", &totNode, &totEdge); Init(totNode); for (int i = 1; i <= totEdge; i++) { scanf("%d%d%d", &uId, &vId, &w); Build(uId, vId, w); } Kruskal(); GetReady(); printf("%lld ", Proceed()); return 0; }