Boruvka算法

Posted lher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Boruvka算法相关的知识,希望对你有一定的参考价值。

Boruvka算法解决某些问题超级好用。

这些问题形如,给你n个点,每个点有点权,任意两个点之间有边权,边权为两个点权用过某种计算方式得出。

求最小生成树。

通常用O(log n)的时间可以找到与点i连边的边权最小的j。

我们考虑这样一个求最小生成树的算法:

考虑维护当前的连通块(初始每个点为独立的一个连通块)

对每个连通块,找到一条与该连通块相连的,且另一端点不在此连通块中的边权最小的边。

将所有的这些边都加入最小生成树,注意,当加入一条边时需判断该边的两端点是否在同一连通块。

重复若干遍上述操作,直到图连通。

这个算法叫Boruvka算法。

复杂度分析:每次连通块个数至少减半,则复杂度为O((n+m)logn),还有个并查集假装是O(1)

但是此算法通常不用于求裸的最小生成树(Kruskal多好用)

可是在解决上述问题时,往往有奇效。

我们发现,我们只需要求出与每个连通块相连的边权最小的边即可,在这种类型的题目中,这个东西复杂度一般为O(n log n)

所以我们就可以在O(n log^2 n)的复杂度下解决此类问题。

Atcoder keyence2019 E

#include <cstdio>
#include <iostream>
#include <cstring>
#define MN 201000

typedef long long ll;
int fa[MN];
int c1[MN], c2[MN];
int Min[MN];
ll D; int a[MN];
int X[MN], Y[MN];
int x[MN], y[MN];
ll ans = 0;
int bl;
int n;

int Abs(int a) {return a > 0 ? a : -a;}
ll F(int i) {return i ?  i * D + a[i] : 1e18;}
ll G(int i) {return i ? -i * D + a[i] : 1e18;}
ll H(int i, int j) {if(i == 0 || j == 0) return 1e18; return Abs(i - j) * D + a[i] + a[j];}

void add(int *c, int x, int v, int type)
{
    for(int i = x; i <= n; i += i & -i)
    {
        if(!type) if(F(v) < F(c[i])) c[i] = v;
        if( type) if(G(v) < G(c[i])) c[i] = v;
    }
}

int query(int *c, int x, int type)
{
    int ans = 0;
    for(int i = x; i; i -= i & -i)
    {
        if(!type) if(F(c[i]) < F(ans)) ans = c[i];
        if( type) if(G(c[i]) < G(ans)) ans = c[i];
    }
    return ans;
}

int Find(int x) {return fa[x] == x ? x : fa[x] = Find(fa[x]);}

void solve()
{
    memset(x, 0, sizeof x);
    memset(y, 0, sizeof y); 
    memset(c1, 0, sizeof c1);
    memset(c2, 0, sizeof c2);
    memset(Min, 0, sizeof Min);
    for(int i = 1; i <= n; i++)
    {
        int u = Find(i);
        int x = query(c1, u - 1, 1);
        int y = query(c2, n - u, 1);
        if(G(x) > G(y)) x = y;
        if(H(x, i) <= H(Min[i], i)) Min[i] = x;
        add(c1, u, i, 1);
        add(c2, n - u + 1, i, 1);
    }
    memset(c1, 0, sizeof c1);
    memset(c2, 0, sizeof c2);
    for(int i = n; i >= 1; i--)
    {
        int u = Find(i);
        int x = query(c1, u - 1, 0);
        int y = query(c2, n - u, 0);
        if(F(x) > F(y)) x = y;
        if(H(x, i) <= H(Min[i], i)) Min[i] = x;
        add(c1, u, i, 0);
        add(c2, n - u + 1, i, 0);
    }
    for(int i = 1; i <= n; i++)
    {
        int u = Find(i);
        if(H(i, Min[i]) < H(x[u], y[u])) x[u] = i, y[u] = Min[i];
    }
    int tot = 0;
    for(int i = 1; i <= n; i++)
    {
        int u = Find(i);
        ++tot; X[tot] = x[u]; Y[tot] = y[u];
    }
    for(int i = 1; i <= tot; i++)
    {
        int x = Find(X[i]), y = Find(Y[i]);
        if(x == y) continue;
        ans += H(X[i], Y[i]);
        fa[x] = y;
        bl--;
    }
}

int main()
{
    scanf("%d%lld", &n, &D);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]), fa[i] = i;
    bl = n;
    while(bl > 1) solve();
    printf("%lld
", ans);
}

 

以上是关于Boruvka算法的主要内容,如果未能解决你的问题,请参考以下文章

Boruvka算法

查找最小生成树:克鲁斯克尔算法(Kruskal)算法

克鲁斯卡尔算法(Kruskal算法)(最小生成树算法)-贪心

克鲁斯卡尔算法(Kruskal算法)(最小生成树算法)-贪心

UOJ176新年的繁荣

Boruvka最小生成树模板