zoukankan      html  css  js  c++  java
  • luogu4180 次小生成树Tree 树上倍增

    题目:求一个无向图的严格次小生成树(即次小生成树的边权和严格小于最小生成树的边权和)

    首先求出图中的最小生成树。任意加一条树外边都会导致环的出现。我们现在目标是在树外边集合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;
    }
    View Code
  • 相关阅读:
    漫谈程序猿系列:看看你离优秀有多远
    qt学习笔记(五) QGraphicsPixmapItem与QGraphicsScene的编程实例 图标拖动渐变效果
    DropdownList绑定的两种方法
    JUnit入门
    [rxjs] Async, handle data over time
    [rxjs] Creating An Observable with RxJS
    [Javascript + rxjs] Simple drag and drop with Observables
    [Javascript + rxjs] Using the map method with Observable
    [AngularJS] Extract predicate methods into filters for ng-if and ng-show
    [Javascript + rxjs] Introducing the Observable
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8455346.html
Copyright © 2011-2022 走看看