首页 > 其他分享 >Kruskal重构树 学习笔记

Kruskal重构树 学习笔记

时间:2022-12-30 10:58:10浏览次数:46  
标签:重构 int text Kruskal 笔记 瓶颈 节点

最小/大瓶颈路

在探究何为 \(\text{Kruskal}\) 重构树之前,我们先要了解何为最小/大瓶颈路。

简单来说,\(a\) 到 \(b\) 的最小瓶颈路指的是一条简单路径使得 \(a\) 到 \(b\) 的最大边权最小,最大瓶颈路反之。

定义

\(\text{Kruskal}\) 重构树是如此构建的,类似 \(\text{Kruskal}\) 算法的过程:

  1. 对所有边进行排序
  2. 从小到大(此处只讨论最小瓶颈路,最大瓶颈路同理)枚举所有边。
    1. 如果此边连接的两点在同一集合中,那么跳过这条边。
    2. 如果此边连接的两点 \(a, b\) 不在同一集合中,那么把这个边权 “提起来” 使它成为一个新建虚点的点权,此虚点两个子节点分别为 \(a, b\) 所在的集合。
  3. 不断迭代步骤 \(2\) 直到所有边被遍历完。

由此得到了一棵 \(\text{Kruskal}\) 重构树。

可以来模拟一下过程:

如果有这么一张图,对它构建 \(\text{Kruskal}\) 重构树,过程如下:

  1. 排序:

    排序好的的边 \((u, v, w)\) 如下:\(\color{red}{(1, 3, 1), (2, 4, 1), (4, 5, 1)}, \color{orange}{(1, 5, 2), (2, 3, 2)},\color{green} (5, 6, 3),\color{blue} (3, 5, 4)\)(边权相同的边已用相同颜色标注)。

  2. 从小到大枚举所有边:

得到最后的重构树。

性质

\(\text{Kruskal}\) 重构树中的方节点的权值可以这么理解:

如果令方节点为 \(p\),点权为 \(w\),两个子树中圆节点组成的集合为 \(S_{left}\) 与 \(S_{right}\)。定义集合 \(A, B\) 的距离为 \(dist[A][B]\),表示 \(A\) 中所有点到 \(B\) 中所有点距离的最小值,类似 \(\text{Prim}\) 算法中集合距离的定义。

则点权 \(w\) 一定满足 \(dist[S_{left}][S_{right}]\)。

由于已经对边进行了排序,因此第一个连通 \(S_{left}\) 与 \(S_{right}\) 的边一定是所有连通 \(S_{left}\) 与 \(S_{right}\) 中最小的。

通过这个思路,我们可以简单证明 \(\text{Kruskal}\) 重构树的一些性质:

  1. \(\text{Kruskal}\) 重构树是二叉树

即一个点最多有两个子树,显然,一条边最多能连接两个集合,得证。

  1. 方节点满足堆的性质。

即深度小的节点权重一定 \(\geq\) 深度大的节点权重。因为深度大的节点一定比深度小的节点先枚举到,而先枚举意味着它一定不严格大于后枚举的元素。

  1. \(\text{Kruskal}\) 重构树的根节点一定是最后一个处理的方节点。

由性质2可知,根节点一定是所有节点中点权最大者,最后一个处理的边一定最大,因此最后一个处理的方节点一定是根节点。

  1. 任意两点的 \(\text{LCA}\) 就是原图中它们最小瓶颈路中的最大边权。

继续拿这棵树举例子,假设我们要求找到 \(1\) 和 \(5\) 之间的最小瓶颈路中的最大边权,那么它们的 \(\text{LCA}\) 即为方点 \(2\)。

\(\text{LCA}\) 指最近公共祖先,那么现在就有了两个问题:

  1. 为什么这个方点一定是公共祖先?

    可以把走重构树类比成一个 \(\text{Kruskal}\) 的过程,选取了一个方点意味着选取了它子树中所有的方点(边),此方点连通了它两个子树中的圆点集合,因此只有 \(1\) 和 \(5\) 的公共祖先才能使 \(1\) 和 \(5\) 连通。

  2. 为什么这个方点一定是最近的公共祖先?

    因为 \(\text{Kruskal}\) 重构树第二个性质:“方节点满足堆的性质”,所以在保证连通的情况下(等价于是 \(1\) 和 \(5\) 的公共祖先),要使最大边权最小,这就要求使得方节点点权尽可能地小,深度尽可能地大,也就是最近的公共祖先——\(\text{LCA}\)。

\(\text{Kruskal}\) 重构树最重要的性质就是 “任意两点的 \(\text{LCA}\) 就是原图中它们最小瓶颈路中的最大边权”,根据它我们可以轻松解决最小瓶颈路问题。

应用

[NOIP2013 提高组] 货车运输(最大瓶颈路)

BZOJ3732 Network(最小瓶颈路)

这两题都是 \(\text{Kruskal}\) 重构树的板子题,基本是一模一样的。

此处是Network的代码,\(\text{LCA}\) 用倍增实现。

// Author: Moyou
// Copyright (c) 2022 Moyou All rights reserved.
// Date: 2022-12-30 09:24:04

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;

const int N = 6e4 + 10, M = 1e5 + 10, K = 2e4 + 10;

struct E
{
    int a, b, c;
} edge[M];
int n, m, T;

int fa[N], w[N], idx;
int depth[N], f[N][25];
int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

vector<int> g[N + M];

void build() // 构建Kruskal重构树
{
    cin >> n >> m >> T;
    for (int i = 1; i <= 2 * n; i++)
        fa[i] = i;
    for (int i = 1; i <= m; i++)
        cin >> edge[i].a >> edge[i].b >> edge[i].c;
    sort(edge + 1, edge + m + 1, [](E a, E b) { return a.c < b.c; }); // 由于是最小瓶颈路所以从小到大排序
    idx = n; // 记录方节点编号
    for (int i = 1; i <= m; i++)
    {
        int x = find(edge[i].a), y = find(edge[i].b);
        if (x == y)
            continue;
        w[++idx] = edge[i].c;                     // 记录方节点点权
        g[idx].push_back(x), g[idx].push_back(y); // 连方节点两棵子树
        fa[x] = fa[y] = idx;
    }
}

void depth_dfs(int p, int pa) // 倍增LCA,预处理深度与倍增数组
{
    depth[p] = depth[pa] + 1;
    f[p][0] = pa;

    for (int i = 1; i <= 20; i++)
        f[p][i] = f[f[p][i - 1]][i - 1];
    for (auto i : g[p])
    {
        if (pa == i)
            continue;
        depth_dfs(i, p);
    }
}

int lca(int a, int b) // LCA
{
    if (depth[a] < depth[b])
        swap(a, b);
    for (int i = 19; i >= 0; i--)
        if (depth[f[a][i]] >= depth[b]) // 先跳到同一高度
            a = f[a][i];

    if (a == b)
        return a;
    for (int i = 19; i >= 0; i--) // 再一起往上跳
        if (f[a][i] != f[b][i])
            a = f[a][i], b = f[b][i];

    return f[a][0];
}

int main()
{
    build();
    depth[idx] = 1, depth[0] = 0;
    depth_dfs(idx, 0);
    while (T--)
    {
        int a, b;
        cin >> a >> b;
        cout << w[lca(a, b)] << '\n';
    }

    return 0;
}

标签:重构,int,text,Kruskal,笔记,瓶颈,节点
From: https://www.cnblogs.com/MoyouSayuki/p/17014320.html

相关文章

  • 学习笔记之布置简单的云服务器
    最近有个项目需要在云服务器上布置进行测试,因为项目还处于立项阶段,就打算找个免费的云服务器测试一下。测试免费云服务过程记录。(1)安装一个ubuntu16系统,在本地远程登陆服......
  • 学习笔记之免费云服务器
    因为项目需求,了解到了这个sanfengyun网站,可以申请免费虚拟主机和免费云服务器。申请到免费云服务器后,可以看到自己的控制台主页里有所有的信息。(其中网址如下图所示) ......
  • 雅思口语笔记——杨帅
    雅思口语考试概述1、评分标准评分标准一共有四项,如下所示:可以看出在口语考试中,并没有要求扣题。我们只需要大概扣题到话题上,但是并不需要给出标准答案。譬如问你一个......
  • 【Python】爬虫笔记-requests.exceptions.ProxyError
    0x01爬虫使用HTTP/HTTPS代理时报故:proxy='127.0.0.1:9743'proxies={'http':'http://'+proxy,'https':'https://'+proxy,}response=requests.ge......
  • 刷题笔记——1112:C语言考试练习题_一元二次方程
    题目1112:C语言考试练习题_一元二次方程代码importmathwhileTrue: try: a,b,c=map(float,input().strip().split()) delta=b*b-4*a*c x1=(-b+......
  • 详解聚类算法Kmeans-重要参数init & random_state & n_init:初始质心怎么放更好【菜菜
    视频作者:菜菜TsaiTsai链接:【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibiliinit在K-Means中有一个重要的环节,就是放置初始质心。如果有足够......
  • 详解网络层-网络层概述和编址【王道计算机网络笔记】
    主要任务是把分组从源端传到目的端,为分组交换网上的不同主机提供通信服务。网络层的传输单位是数据报,数据报是一个比较长的数据,分组是对数据报进行切割得到的一部分功能:......
  • JS笔记(二):数据类型
    镇楼图Pixiv:torino三、数据类型原始类型原始类型像是string、symbol、number之类的都只能存储原子值,而不能像对象一样随意扩展。但是为了提供额外功能,采取了轻量的......
  • Web前端学习笔记3——列表与表单
    无序列表无序列表的标签:<ul></ul>无序列表列表项的标签:<li></li>ul标签中只能嵌套li标签,不能存放别的标签或者数字,li标签之中可以存放任何元素和标签无序列表会默认在......
  • 十二月第二份阅读笔记
    本次阅读了从小工到专家这本书的第八章:注重实效的项目,同时也是本书的最后章节,本章节由注重实效的团队,无处不在的自动化,无情的测试,全都是写,极大的期望,傲慢与偏见这六部分组......