luogu1967 货车运输 最大瓶颈生成树

Posted headboy2002

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu1967 货车运输 最大瓶颈生成树相关的知识,希望对你有一定的参考价值。

题目大意

给出一张图,给出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 货车运输 最大瓶颈生成树的主要内容,如果未能解决你的问题,请参考以下文章

LUOGU P1967 货车运输(最大生成树+树剖+线段树)

luogu1967noip2013 货车运输 [生成树kruskal LCA ]

luogu P1967 货车运输 最大生成树 倍增LCA

$Noip2013/Luogu1967$ 货车运输 最大生成树+倍增$lca$

Luogu P1967 货车运输题解

luogu1967[NOIP2013D1T3] 货车运输 (最大生成树+LCA)