• 【洛谷】P3377 【模板】左偏树(可并堆)


    题目地址:

    https://www.luogu.com.cn/problem/P3377

    题目描述:
    如题,一开始有 n n n个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:
    1、1 x y:将第 x x x个数和第 y y y个数所在的小根堆合并(若第 x x x或第 y y y个数已经被删除或第 x x x和第 y y y个数在用一个堆内,则无视此操作)。
    2、2 x:输出第 x x x个数所在的堆最小数,并将这个最小数删除(若有多个最小数,优先删除先输入的;若第 x x x个数已经被删除,则输出 − 1 -1 1并无视删除操作)。

    输入格式:
    第一行包含两个正整数 n , m n, m n,m,分别表示一开始小根堆的个数和接下来操作的个数。
    第二行包含 n n n个正整数,其中第 i i i个正整数表示第 i i i个小根堆初始时包含且仅包含的数。
    接下来 m m m行每行 2 2 2个或 3 3 3个正整数,表示一条操作,格式如下:
    操作 1 1 11 x y
    操作 2 2 22 x

    输出格式:
    输出包含若干行整数,分别依次对应每一个操作 2 2 2所得的结果。

    数据范围:
    对于 30 % 30\% 30%的数据: n ≤ 10 n\le 10 n10 m ≤ 10 m\le 10 m10
    对于 70 % 70\% 70%的数据: n ≤ 1 0 3 n\le 10^3 n103 m ≤ 1 0 3 m\le 10^3 m103
    对于 100 % 100\% 100%的数据: n ≤ 1 0 5 n\le 10^5 n105 m ≤ 1 0 5 m\le 10^5 m105,初始时小根堆中的所有数都在int范围内。

    参考https://blog.csdn.net/qq_46105170/article/details/126204599。可以将删除了的点的距离值 d d d设为 0 0 0。代码如下:

    #include 
    using namespace std;
    
    const int N = 1e5 + 10;
    int n, m;
    int v[N], d[N], l[N], r[N];
    int p[N];
    
    bool cmp(int x, int y) {
      if (v[x] != v[y]) return v[x] < v[y];
      return x < y;
    }
    
    int find(int x) {
      if (p[x] != x) p[x] = find(p[x]);
      return p[x];
    }
    
    int merge(int x, int y) {
      if (!x || !y) return x ^ y;
      if (!cmp(x, y)) swap(x, y);
      r[x] = merge(r[x], y);
      if (d[r[x]] > d[l[x]]) swap(l[x], r[x]);
      d[x] = d[r[x]] + 1;
      return x;
    }
    
    int main() {
      scanf("%d%d", &n, &m);
      for (int i = 1; i <= n; i++) {
        p[i] = i;
        d[i] = 1;
        scanf("%d", &v[i]);
      }
    
      while (m--) {
        int t, x, y;
        scanf("%d%d", &t, &x);
        if (t == 1) {
          scanf("%d", &y);
          if (!d[x] || !d[y]) continue;
          x = find(x), y = find(y);
          if (x == y) continue;
          if (!cmp(x, y)) swap(x, y);
          p[y] = x;
          merge(x, y);
        } else {
          if (!d[x]) {
            puts("-1");
            continue;
          }
          x = find(x);
          printf("%d\n", v[x]);
          // 标记x为已经被删除
          d[x] = 0;
          // 如果x不是叶子,并且左儿子更适合做堆顶(值小或者值等下标小),则交换左右儿子,
          // 这是为了防止空节点被换到左边
          if (r[x] && !cmp(l[x], r[x])) swap(l[x], r[x]);
          p[x] = p[l[x]] = l[x];
          merge(l[x], r[x]);
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    每次操作时间复杂度 O ( log ⁡ n ) O(\log n) O(logn),空间 O ( n ) O(n) O(n)

  • 相关阅读:
    keycloak~网站对接到Keycloak的步骤
    大小端存储是什么鬼?
    php初级教程三 文件
    入门力扣自学笔记121 C++ (题目编号1282)
    CDD文件——CANdelaStudio
    大数据技术基础实验十一:Hive实验——Hive分区
    ABAP BP维护客户cl_md_bp_maintain=>maintain
    使用 OpenCV 和 Tesseract OCR 进行车牌识别
    c++ 并发与多线程(12)线程安全的单例模式-1
    CrossOver2023(Mac电脑运行Windows软件)
  • 原文地址:https://blog.csdn.net/qq_46105170/article/details/126205297