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

Kruskal重构树 学习笔记

时间:2023-09-15 19:55:08浏览次数:40  
标签:重构 ch const int Kruskal up 笔记 边权

Kruskal 重构树

最大生成树将部分内容倒置即可

回顾:Kruskal

基本信息

  1. 求解最小生成树
  2. 时间复杂度:\(O(m \log m)\)
  3. 更适合稀疏图

算法思想

  1. 按照边权从小到大排序
  2. 依次枚举每一条边,如果这一条边两侧不连通,则加入这条边

代码

点击查看代码
#include <bits/stdc++.h>

#define rr read()

using namespace std;

const int N = 200010;

inline int read()
{
    int num = 0, flag = 1;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            flag = -1;
    for (; isdigit(ch); ch = getchar())
        num = (num << 3) + (num << 1) + ch - '0';
    return num * flag;
}

int f[N];

struct Edge
{
    int a, b, w;
    bool operator<(const Edge &W) const { return w < W.w; }
} g[N];

int find(int x) { return x == f[x] ? x : find(f[x]); }

int main()
{
    int n = rr, m = rr;

    int a, b, w;
    for (int i = 0; i < m; ++i)
        a = rr, b = rr, w = rr, g[i] = {a, b, w};

    sort(g, g + m);

    for (int i = 1; i <= n; ++i)
        f[i] = i;

    int res = 0, cnt = 0;
    for (int i = 0; i < m; ++i)
    {
        int a = find(g[i].a), b = find(g[i].b), w = g[i].w;
        if (a != b)
            f[a] = b, res += w, ++cnt;
    }

    cnt < n - 1 ? printf("impossible\n") : printf("%d\n", res);
    return 0;
}

Kruskal 重构树

算法思想

在构建最小生成树的时候,设现在枚举到了一条要加入最小生成树的边 \((u, v, w)\):

则在 Kruskal 重构树中,构建一个点权为 \(w\) 的虚点,编号为 \(t\),同时连边 \((u, t)\)、\((v, t)\)。

主要性质

  1. 重构树是一棵[二叉树];
  2. [子节点的点权]小于[父节点的点权](即大根堆);
  3. 最小生成树上[两点之间的最大边权]等于重构树上[两点之间的最大边权](即为重构树上两点 LCA 的点权)。

结论证明

最小生成树上两点间最大边权等于重构树上两点 LCA 的点权,证明:

  1. 后加入的边权一定小于先加入的边权,所以重构树一定自上到下点权不减;
  2. 两点在最小生成树上的路径的所有边一定都在重构树上两点之间;
  3. 所以两点在最小生成树上之间的最长边权一定是重构树上两点 LCA 的点权。

如图:

其中红色的点表示虚点,中间的数字表示其点权;白色的点表示原有的点。

代码

#include <bits/stdc++.h>

#define rr read()

using namespace std;

// INPUT GRAPH
const int N = 2e5 + 10;
const int M = 2e5 + 10;

// NEW GRAPH
const int NN = N + M;
const int MM = M + M;

// 4LCA
const int K = 20;

// FAST READ
inline int read()
{
    int num = 0, flag = 1;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            flag = -1;
    for (; isdigit(ch); ch = getchar())
        num = (num << 3) + (num << 1) + ch - '0';
    return num * flag;
}

// NODE, EDGE, QUERY
int n, m, q;

// INPUT GRAPH
struct e
{
    int u, v, w;
    bool operator<(const e &t) const { return w < t.w; }
} g[M];

// UNOIN
int f[NN];
int find(int x) { return x == f[x] ? x : find(f[x]); }

// NEW GRAPH
int d[NN], cnt;
int h[NN], e[MM], ne[MM], idx;

// 4LCA
int depth[NN];
int up[NN][K];

// ADD TO NEW GRAPH
inline void _add(int u, int v)
{
    e[idx] = v;
    ne[idx] = h[u];
    h[u] = idx++;
}

void add(int a, int b, int w)
{
    d[++cnt] = w;
    f[a] = f[b] = cnt;
    _add(a, cnt), _add(cnt, a);
    _add(b, cnt), _add(cnt, b);
}

// LCA INIT
void init(int u, int fa)
{
    depth[u] = depth[fa] + 1;
    for (int i = 1; i < K; ++i)
        up[u][i] = up[up[u][i - 1]][i - 1];

    for (int i = h[u]; i != -1; i = ne[i])
    {
        int v = e[i];
        if (v == fa)
            continue;
        up[v][0] = u, init(v, u);
    }
}

// KRUSKAL
int kruskal()
{
    sort(g + 1, g + 1 + m);

    for (int i = 1; i <= n * 2; ++i)
        f[i] = i;

    cnt = n;
    memset(h, -1, sizeof h);

    int res = 0;
    for (int i = 1; i <= m; ++i)
    {
        int u = find(g[i].u), v = find(g[i].v), &w = g[i].w;
        if (u == v)
            continue;
        res += w, add(u, v, w);
    }

    init(cnt, 0);
    return res;
}

// LCA
int lca(int x, int y)
{
    if (depth[x] < depth[y])
        swap(x, y);

    for (int i = K - 1; i >= 0; --i)
    {
        if (depth[up[x][i]] >= depth[y])
            x = up[x][i];
        if (x == y)
            return x;
    }

    for (int i = K - 1; i >= 0; --i)
        if (up[x][i] != up[y][i])
            x = up[x][i], y = up[y][i];
    return up[x][0];
}

int main()
{
    n = rr, m = rr;

    int a, b, w;
    for (int i = 1; i <= m; ++i)
        a = rr, b = rr, w = rr, g[i] = {a, b, w};

    q = rr;

    int res = kruskal();
    while (q--)
        printf("%d\n", d[lca(rr, rr)]);
    return 0;
}

资料来源

标签:重构,ch,const,int,Kruskal,up,笔记,边权
From: https://www.cnblogs.com/RainPPR/p/kruskal-zhong-gou-shu.html

相关文章

  • 如何提升代码质量,重构并非“万能药”
    随着编程技术的不断进步,编程语言变得越来越高级,功能封装也越来越完善。各种技术都在帮助程序员提高编写代码的效率。通过层层封装,程序员似乎不需要了解技术细节,只需逐行翻译需求内容即可。许多程序员不了解如何组织代码、提升运行效率以及底层基于的原理是什么,但是他们编写的代码......
  • 《信息安全系统设计与实现》第二周学习笔记
    《信息安全系统设计与实现》第二周学习笔记第九章I/O库函数系统调用系统调用函数open()read()write()lseek()close()I/O库函数fopen()fread()fwrite()fseek()fclose()I/O库函数的算法fread算法:第一次调用fread()时候,FILE结构体的缓冲区时空的,fread(......
  • 3dmax自用快捷键笔记
    3dmax自用快捷键笔记G隐藏或显示网格ALT+W全屏模式ALT+Q独立显示ALT+X物体透明显示W移动Z找回物体缩放模式(大化)CTRL+Z撤回(最多撤回9步)F1帮助文件F3线框显示F4明暗处理+边面M材质编辑器J框显示切换A角度捕捉S......
  • Qemu源码分析(2)—Apple的学习笔记
    一,前言最近从main开始看了opt参数相关的解析,这个比较简单我就不写了,然后当时我搞不清楚的是MachineClass和TypeImpl类的关系。本节主要分析的其实就是分析machine_class怎么来的,其实也就是machine_class=select_machine();二,源码分析关于mc的来历type_initialize中ti->class->ty......
  • FATs文件系统笔记
    1.设备状态获取点击查看代码DSTATUSdisk_status(BYTEpdrv/*物理编号*/){DSTATUSstatus=STA_NOINIT;switch(pdrv){caseATA:/*SDCARD*/break;caseSPI_FLASH:/*SPIFlash状态检测:读取SPIFlash设备ID*/......
  • linux里python读写mssql数据库的笔记
    1、安装pyodbcpip3installpyodbc我用的debian12,可以直接aptinstallpython3-pyodbc2、还需要安装linux版的mssqlclient参考这里:https://learn.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver......
  • UE4 笔记
    1.FString转TCharTChar*c=(*FString)2.TChar*与char*的互相转换,主要是使用下面的四个宏定义。TCHAR_TO_ANSI(str)ANSI_TO_TCHAR(str)TCHAR_TO_UTF8(str)UTF8_TO_TCHAR(str)[C4668]没有将_WIN32_WINNT_WIN10_TH2"定义为预处理器宏,用0“替换"#if/#elif"添......
  • 【刷题笔记】51. N-Queens
    题目Then-queenspuzzleistheproblemofplacingnqueensonann×nchessboardsuchthatnotwoqueensattackeachother.Givenaninteger n,returnalldistinctsolutionstothe n-queenspuzzle.Eachsolutioncontainsadistinctboardconfigurationoft......
  • HBase/Hadoop学习笔记 (转)
    HBase/Hadoop学习笔记  学习目标:至少掌握五点: 1.    深入理解HTable,掌握如何结合业务涉及高性能的HTable。 2.    掌握与HBase的交互,通过HBaseShell命令及JavaAPI进行数据的增删改查。 3.    掌握如何用MapReduce分析HBase里的数据 4.    掌握如何测试HB......
  • 20211326德永学习笔记2
    第九章总结要点1.I/O库函数与系统调用系统调用函数:open()、read()、write()、lseek()、close();I/O库函数:fopen()、fread()、fwrite()、flseek()、fclose()。I/O库函数一一对应地依赖于系统调用函数。2、I/O库函数的算法-2.1fread算法在第一次调用fread()时,FILE结构体的缓......