博客
关于我
并查集+线段树合并:P3224-永无乡
阅读量:198 次
发布时间:2019-02-28

本文共 2561 字,大约阅读时间需要 8 分钟。

并查集与线段树结合解决集合中的第k大权值查询问题

问题描述

在处理一系列权值点的集合操作时,我们需要在合并两个集合时,能够快速查询其中的第k大权值。传统的并查集难以满足这一需求,而结合并查集与线段树的方法可以有效解决问题。

解决方案

我们采用并查集结构,每个集合对应一个线段树。这样,在合并两个集合时,可以直接将对应的线段树合并,确保在后续查询时能够快速找到第k大权值。

技术实现

并查集部分

  • 并查集结构:使用路径压缩和按秩合并的优化方法,确保每次操作的时间复杂度较低。
  • 父节点数组:用于记录每个节点的父节点,路径压缩能有效降低查找时间。
  • 代表数组:用于存储每个集合的代表节点,避免多次查找相同父节点。
  • 线段树部分

  • 线段树节点结构:每个节点存储左、右子树指针以及区间的权值和。
  • 构建线段树:从1到n构建线段树,初始化每个节点的和。
  • 合并操作:在合并两个集合时,同时合并对应的线段树,使得查询操作能够快速获取第k大权值。
  • 查询操作:使用线段树的特性,快速定位第k大权值。
  • 代码解析

    线段树代码

    #include 
    using namespace std;#define mid ((l + r) >> 1)const int maxn = 1e5 + 5;int sum[maxn < 5], ls[maxn < 5], rs[maxn < 5], rt[maxn], tot;void pushup(int t) { int tl = ls[t], tr = rs[t]; sum[t] = sum[tl] + sum[tr];}int add(int l, int r, int t, int p, int c) { int now = ++tot; ls[now] = ls[t]; rs[now] = rs[t]; if (l == r) { sum[now] += c; return now; } if (p <= mid) { ls[now] = add(l, mid, ls[now], p, c); } else { rs[now] = add(mid + 1, r, rs[now], p, c); } pushup(now); return now;}int ask(int l, int r, int t, int k) { if (l == r) return l; if (sum[ls[t]] >= k) { return ask(l, mid, ls[t], k); } else { return ask(mid + 1, r, rs[t], k - sum[ls[t]]); }}int mer(int a, int b, int l, int r) { if (!a) return b; if (!b) return a; if (l == r) { sum[a] += sum[b]; return a; } ls[a] = mer(ls[a], ls[b], l, mid); rs[a] = mer(rs[a], rs[b], mid + 1, r); pushup(a); return a;}

    并查集代码

    int a[maxn], f[maxn], n, m, id[maxn];int getf(int x) {    return f[x] == x ? x : f[x] = getf(f[x]);}bool dsu_mer(int a, int b) {    int fa = getf(a), fb = getf(b);    if (fa == fb) return false;    f[fb] = fa;    mer(rt[fa], rt[fb], 1, n);    return true;}

    主函数

    int main() {    scanf("%d%d", &n, &m);    for (int i = 1; i <= n; i++) {        scanf("%d", a + i);        id[a[i]] = i;        f[i] = i;        rt[i] = add(1, n, rt[0], a[i], 1);    }    for (int i = 1; i <= m; i++) {        int x, y;        scanf("%d%d", &x, &y);        dsu_mer(x, y);    }    char op[5];    int q;    scanf("%d", &q);    for (int i = 1; i <= q; i++) {        scanf("%s", op);        scanf("%d%d", &x, &y);        if (op[0] == 'Q') {            x = getf(x);            if (sum[rt[x]] < y) {                printf("-1\n");            } else {                printf("%d\n", id[ask(1, n, rt[x], y)]);            }        } else {            dsu_mer(x, y);        }    }    return 0;}

    应用示例

    • 输入处理:读取n和m,然后初始化权值数组a。
    • 线段树构建:为每个节点构建线段树,记录每个节点的代表节点id。
    • 并查集操作:处理m次合并操作。
    • 查询操作:处理q次查询,返回第k大权值或-1。

    通过上述方法,我们可以在并查集合并过程中,高效地查询集合中的第k大权值。

    转载地址:http://gadi.baihongyu.com/

    你可能感兴趣的文章
    python 利用pyspark读取HDFS中CSV文件的指定列 列名重命名 并保存回HDFS
    查看>>
    python 利用pyttsx3文字转语音
    查看>>
    python 利用已有Ner模型进行数据清洗合并
    查看>>
    python 到大数据开发工程师_如何成为一个大数据开发工程师?
    查看>>
    python 加密解密(base64, AES)
    查看>>
    Python 包管理器和 Node.js
    查看>>
    Python 单词字母顺序不变且所有倒排
    查看>>
    python 启动提示IDLE's subprocess didn't make conne...
    查看>>
    python 命令接口_实现“[命令][操作][参数]”样式的命令行接口?
    查看>>
    Python 和 OpenCV.如何检测图像中的所有(填充)圆形/圆形对象?
    查看>>
    Python 和 RabbitMQ - 聆听来自多个渠道的消费事件的最佳方式?
    查看>>
    python编辑器打不开_关于命令ride.py打不开RF,而是打开pycharm编辑器问题解决思路...
    查看>>
    python编译exe同时支持32位_第三周:同时管理64位和32位版本的Python,并用Pyinstaller打包成exe...
    查看>>
    Python 和Java 哪个更适合做自动化测试?
    查看>>
    Python编程:掌握高级语言程序设计。从零基础到精通,收藏这篇就够了!
    查看>>
    python 图片转ico
    查看>>
    python 图片转文字、语音转文字、文字转语音保存音频并朗读
    查看>>
    python 在包含类似字符\x16、\x12、\x某某的数组中将以\x开头的字符找出来的方法
    查看>>
    Python 在并行进程之间共享字典
    查看>>
    Python 垃圾收集器文档
    查看>>