Kruskal重构树详解

目录

Kruskal算法

Kruskal重构树


在学习重构树之前,我们要先熟悉一下基本的kruskal算法

Kruskal算法

首先给出一张有向图,让我们求最小生成树(用总权值最小的一些边的集合,使得所有点都能互通,很明显n个点会有n-1条边)

Kruskal重构树详解_第1张图片

kruskal算法思想是先把所有的边按权值大小排序,得到这个样子

Kruskal重构树详解_第2张图片

然后从小往大依次取边,如果加上这条边和之前的边构成了环,就舍弃这条边,不然就加上,直至取得n-1条边,构成一棵树

例如此图,我们按照权值加完前四条边

Kruskal重构树详解_第3张图片

在加入第五条边时,我们发现原图出现了环,我们就舍弃这条边

Kruskal重构树详解_第4张图片

就这样一直加边,直至构成一棵完整的树

Kruskal重构树详解_第5张图片

这就是kruskal算法的

Kruskal重构树

那么什么又是kruskal重构树呢?

kruskal重构树其实就是把这张图重建成一个二叉树,原图中所有的叶子节点都为原图中的点

其他点都有一个点权w,代表从左集合到右集合的路径,由于重构树是在kruskal的基础上完成的,所以我们必然可以得到一个自下而上点权不降的二叉树

比如我们还用上图距离,建一颗重构树

首先我们合并6,7两个集合,把他们两个连接到一个虚点上,该点权值为他们的边权

Kruskal重构树详解_第6张图片

(红色代表虚点,白色代表原图的点)

然后我们依次2-8 , 5-6 

Kruskal重构树详解_第7张图片

就这样把这颗二叉树建完

Kruskal重构树详解_第8张图片

最后重构树大概长这样

这棵树有很多独特的性质

就比如我们想知道从节点2到7路径的最大边权是多少,其实就是他们公共祖先这个虚点的点权值

再比如我们想知道从某个点出发,给出一个值,在通过所有边的权值都小于等于这个值时,我们走过多少个点,其实就等于这个点一直往上面找,找到最后一个小于等于这个值的虚点,他的子树的点我们都是可以通过的(因为从上到下点权不上升)

以上这种问题我们都可以通过在重构树上倍增的方式,把时间复杂度压到logn

典型例题有洛谷的P2245 星际导航 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

和去年icpc上海区域赛的H Problem - H - Codeforces

重构树板子:

int n, m;
int k, T, S, D;
struct edge
{
    int a, b, w;
    bool operator <(const edge& ee)const { return w < ee.w; }
}ed[M];
vector e[N];//即为重构树
int d[N];
int p[N];
int a[N];
int down[N];
int fa[N][18];
int find(int x) {
    return p[x] == x ? p[x] : p[x] = find(p[x]);
}
int kruskal() {
    sort(ed + 1, ed + 1 + m);
    fer(i, 1, 2*n)p[i] = i;
    int cnt = n;
    fer(i, 1, m) {
        int a = find(ed[i].a), b = find(ed[i].b);
        if (a^b){
            d[++cnt]=ed[i].w;
            p[a]=p[b]=cnt;
            e[cnt].push_back(a);
            e[cnt].push_back(b);
        }
    }
    return cnt;
}

 

 

 

你可能感兴趣的:(算法,重构,图论,算法)