图论——Kruskal重构树 学习笔记

这篇具有很好参考价值的文章主要介绍了图论——Kruskal重构树 学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

图论——Kruskal 重构树

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

回顾:Kruskal

基本信息

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

算法思想

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

代码

点击查看代码
const int N = 200010;

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 的点权。

如图:

图论——Kruskal重构树 学习笔记

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

代码

// 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;

// 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 : f[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;
}

Reference

[1] https://www.luogu.com.cn/blog/lizbaka/kruskal-chong-gou-shu
[2] https://blog.csdn.net/m0_61735576/article/details/124804973文章来源地址https://www.toymoban.com/news/detail-709963.html

到了这里,关于图论——Kruskal重构树 学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【蓝桥杯--图论】最小生成树prim、kruskal

    今日语录: 成功不是终点,失败不是致命,勇气才是取胜的关键。

    2024年01月24日
    浏览(48)
  • 【刷题】图论——最小生成树:Prim、Kruskal【模板】

    假设有n个点m条边。 Prim适用于邻接矩阵存的稠密图,时间复杂度是 O ( n 2 ) O(n^2) O ( n 2 ) ,可用堆优化成 O ( n l o g n ) O(nlogn) O ( n l o g n ) 。 Kruskal适用于稀疏图,n个点m条边,时间复杂度是 m l o g ( m ) mlog(m) m l o g ( m ) 。 Prim:遍历n次,每次选择 连通块和外面的点 到连通块距离

    2024年04月13日
    浏览(43)
  • 图论13-最小生成树-Kruskal算法+Prim算法

    基本思想:按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路 具体做法:首先构造一个只含 n 个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中 不产生 回路,直至森林变成一棵树为止。 2.2.1 如果图不联通,直接返回空,该

    2024年02月01日
    浏览(46)
  • acwing算法基础之搜索与图论--kruskal算法

    kruskal算法的关键步骤为: 将所有边按照权重从小到大排序。 定义集合S,表示生成树。 枚举每条边(a,b,c),起点a,终点b,边长c。如果结点a和结点b不连通(用并查集来维护),则将这条边加入到集合S中。 kruskal算法的时间复杂度为O(mlogm),它用来解决稀疏图的最小生成树问题

    2024年02月05日
    浏览(43)
  • 二十九、搜索与图论——克鲁斯卡尔算法(Kruskal 算法,稀疏图)

    解决问题: 多个城市中铺公路,使城市之间可以相互联通,问如何才能让铺设公路的长度最短——铺设的路径即为最小生成树。 思想: 从小到大枚举每条边,从小到大试图将每条边假如生成树,只要这条边对应的两个点不在一个集合,则把这条边加到集合中来。 主要面对的

    2024年02月05日
    浏览(46)
  • 【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal)

    最小生成树 有关树的定义 生成子图 :生成子图是从原图中选取部分节点以及这些节点之间的边所组成的图。生成子图中的所有节点和边都必须在原图中存在。 生成树 :一个连通 无向图 的 生成子图 ,同时要求是树。也即在图的边集中选择 n - 1 条,将所有顶点连通。 我们

    2024年02月16日
    浏览(40)
  • 【学习笔记】浅谈最小生成树及重构树

    板子传送门 生成树 一个连通图的生成树是一个极小的连通子图,它包含图中全部的 n n n 个顶点,但只有构成一棵树的 n − 1 n-1 n − 1 条边。 最小生成树 其实就是一个图中最小的一个生成树 所谓一个 带权图 的最小生成树,就是原图中边的 权值最小 的生成树 ,所谓最小是

    2024年02月16日
    浏览(29)
  • 图论基础学习笔记

    这里主要是我自己的一些笔记,帮助我自己快速记忆所用,可能没有定义这么完美。 简单图:无环无平行边的图。下图: 左环右平行边 平凡图: G = ( 1 , 0 ) G=(1,0) G = ( 1 , 0 ) 零图: G = ( p , 0 ) G=(p,0) G = ( p , 0 ) 边数: e ≤ C n 2 eleq C_n^2 e ≤ C n 2 ​ 补图:对于 G = ( V , E ) G=(V,

    2024年02月09日
    浏览(31)
  • C++学习笔记——图论

    图是由 节点(v)和边(e) 组成的,把图记作二元组G=(v,e) 无向图                                                                    有向图 边是双向的,v1可以到v2,v2也能到v1                 边是单向的,v1可以到v2,但v2不能到v1 无权图               

    2024年04月12日
    浏览(28)
  • 图论——最短路 学习笔记

    其实是复习性质的,主要是总结,证明什么的,等上大学再说。 单源最短路:从一个点 (q) 出发,到其他所有点的最短路。 全源最短路:任意两点见最短路。 算法 Floyd Johnson Bellman–Ford SPFA Dijkstra 类型 全源 全源 单源 单源 单源 作用于 任意图 任意图 任意图 任意图 非负权

    2024年02月05日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包