Kruskal重构树入门

Kruskal算法

众所周知,Kruskal是一种优秀的基于并查集的最小(最大)生成树算法。它的简要流程为:

  • 将所有边按边权排序;
  • 依次枚举所有边的两端点 u u u v v v,若它们不在一个联通块内,则将该边加入生成树的边集中,且将 u u u v v v合并于一个连通块中。

Kruskal重构树

定义

Kruskal重构树的定义(构造方式):

  • 一开始,各个结点都在一个以自己为根的连通块中;
  • 执行Kruskal算法,重复以下步骤:
    • 把即将加入生成树边集的边,看做一个新点 x x x x x x的两儿子分别是 u u u所在的连通块对应的根 v v v所在的连通块对应的根 x x x的点权是该边的边权;
    • 合并 u u u v v v,这个合并后的连通块的根是 x x x

举个建树的例子(数据来自于BZOJ3545 [ONTAK2010]Peaks):
注意,最开始的叶子结点的顺序不重要,这里为了好看,重新排列了一下。
举个例子

性质

于是Kruskal重构树的定义就弄清楚了。
下面这个不是动图:
我不是动图

接下来看一下这个东西的性质(接下来只探讨最小生成树):

  • Kruskal重构树是一个二叉树;

废话。

  • Kruskal重构树的叶子没有点权,其他点都有点权;

废话。

  • Kruskal重构树是完全二叉树(除叶结点外,每个结点都有两个儿子);

废话。

  • 原图中的两点 u u u v v v在Kruskal重构树上的LCA的点权是原图中 u u u v v v的所有路径中最大边的最小值。

先考虑:由于是按边权从小到大插入的,所以某个非根非叶子结点的点权一定比它的父亲的点权小;
所以, u u u v v v在Kruskal重构树上的LCA的点权,就是在最小生成树上的 u → v u\to v uv的路径(由于是树,所以是唯一路径)中的最长边;
又因为是最小生成树,所以满足这个最长边最小。

例题

两道板子题

A NOIP2013货车运输
B BZOJ3732 Network
这两道题利用上面的最后一个性质,输出LCA的点权即可。

当然,这两道题也可以直接用最小(最大)生成树来做,用一个倍增数组记录路径上边权的最大值(最小值)就行了。

下面是A题的两种方法的代码:

  • Kruskal重构树(这道题可能不连通= =,详见下面注释的部分)
#include<bits/stdc++.h>
using namespace std;

int read(){
   
    int x=0;bool f=0;char c=getchar();
    while(c<'0'||c>'9') f|=c=='-',c=getchar();
    while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
    return f?-x:x;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值