zoukankan      html  css  js  c++  java
  • 【模板】左偏树

    洛谷模板题

    一听左偏树这个名字就感觉左偏。。

    左偏树是什么,好像就是个堆,大根堆或小根堆,可以支持合并,取堆顶元素,删除堆顶元素,插入元素的操作。

    一些说明:

    左偏树节点除了应有的东西,还有键值和距离,键值用于比较大小,距离是什么?

    距离是这样定义的:

    节点i称为外节点(external node),当且仅当节点i的左子树或右子树为空 ( left(i) = NULL或right(i) = NULL );节点i的距离(dist(i))是节点i到它的后代中,最近的外节点所经过的边数。特别的,如果节点i本身是外节点,则它的距离为0;而空节点的距离规定为-1 (dist(NULL) = -1)。一棵左偏树的距离,指的是该树根节点的距离。

    额。。多看几遍才看懂。

    左偏树具体有一些性质:

    [性质1] 节点的键值小于或等于它的左右子节点的键值。(键值就是点权)——堆的性质

    [性质2] 节点的左子节点的距离不小于右子节点的距离。——左偏

    [性质3] 节点的距离等于它的右子节点的距离加1。(因为左偏,所以右儿子距离小)

    [引理1] 若左偏树的距离为一定值,则节点数最少的左偏树是完全二叉树。  

    [定理1] 若一棵左偏树的距离为k,则这棵左偏树至少有2k+1-1个节点。

    [性质4] 一棵N个节点的左偏树距离最多为ëlog(N+1)û -1。(这是什么鬼?)

    根据性质就可以理解左偏树操作的具体步骤了。

    合并A和B:1.如果有一个树为空就返回另一个

          2.假定root(A).w < root(B).w, 否则交换A和B,把root(A)作为新树的根

          3.合并right(A)和B, 继续步骤2

          4.由于合并之后right(A)的距离可能会变大, 如果变大就交换right(A)和left(A)

          5.由于right(A)距离改变,A的距离也会变,更新dis(A) = dis(right(A)) + 1

     1 //合并以 x, y 为根的堆 
     2 inline int merge(int x, int y)
     3 {
     4     if(!x || !y) return x + y;
     5     if(w[x] > w[y]) swap(x, y);
     6     r[x] = merge(r[x], y);
     7     f[r[x]] = x;//并查集 
     8     if(d[l[x]] < d[r[x]]) swap(l[x], r[x]);
     9     d[x] = d[r[x]] + 1;
    10     return x;
    11 }
    合并

    删除堆顶元素:删除后合并他的两个儿子即可

    1 inline int pop(int x)//返回新合并的堆的堆顶 
    2 {
    3     int lc = l[x], rc = r[x];
    4     f[lc] = lc;
    5     f[rc] = rc;
    6     l[x] = r[x] = d[x] = 0;
    7     return merge(lc, rc);
    8 }
    删除

    取出堆顶元素:更智障

    1 inline int top(int x)//第几个数所在堆的堆顶 
    2 {
    3     return w[x];
    4 }
    取出

    还有一些操作用到了并查集,具体细节看完整代码。

     1 #include <iostream>
     2 #include <cstdio>
     3 
     4 using namespace std;
     5 
     6 int n, m;
     7 int f[100001], l[100001], r[100001], w[100001], d[100001];//f[i]表示第i个数所在堆的堆顶 
     8 bool b[100001];//表示是否被删除 
     9 //合并以 x, y 为根的堆 
    10 inline int merge(int x, int y)
    11 {
    12     if(!x || !y) return x + y;
    13     if(w[x] > w[y]) swap(x, y);
    14     r[x] = merge(r[x], y);
    15     f[r[x]] = x;//并查集 
    16     if(d[l[x]] < d[r[x]]) swap(l[x], r[x]);
    17     d[x] = d[r[x]] + 1;
    18     return x;
    19 }
    20 
    21 inline int pop(int x)//返回新合并的堆的堆顶 
    22 {
    23     int lc = l[x], rc = r[x];
    24     f[lc] = lc;
    25     f[rc] = rc;
    26     l[x] = r[x] = d[x] = 0;
    27     return merge(lc, rc);
    28 }
    29 
    30 inline int find(int x)
    31 {
    32     return x == f[x] ? x : f[x] = find(f[x]);
    33 }
    34 
    35 inline int top(int x)//第几个数所在堆的堆顶 
    36 {
    37     return w[x];
    38 }
    39 
    40 int main()
    41 {
    42     int i, j, x, y, c, fx, fy;
    43     scanf("%d %d", &n, &m);
    44     for(i = 1; i <= n; i++) scanf("%d", &w[i]), f[i] = i;
    45     for(i = 1; i <= m; i++)
    46     {
    47         scanf("%d", &c);
    48         if(c == 1)
    49         {
    50             scanf("%d %d", &x, &y);
    51             if(b[x] || b[y]) continue;//如果有一个数被删除 
    52             fx = find(x);
    53             fy = find(y);
    54             if(fx == fy) continue;//在同一个堆中 
    55             merge(fx, fy);//合并
    56         }
    57         else
    58         {
    59             scanf("%d", &x);
    60             if(b[x]) printf("-1
    ");
    61             else
    62             {
    63                 fx = find(x);
    64                 printf("%d
    ", w[fx]);
    65                 b[fx] = 1;
    66                 f[fx] = pop(fx);
    67                 //因为有的节点指向当前堆的堆顶,所以也得更新删除的堆顶 
    68             }
    69         }
    70     }
    71     return 0;
    72 }
    View Code
  • 相关阅读:
    jquery跨域解决方案JSONP
    javascript的执行顺序
    事件委托
    JSONP解决跨域完整例子
    javascript数组&省市联动分别用js数组和JSON实现
    快速排序
    闭包
    如何解决linux的ssh连接自动断开的问题
    Django 单元测试(简单例子)
    源代码格式化工具推荐(coolformat),可以实现c,c++,c#,java,js,json,html,sql等的格式化
  • 原文地址:https://www.cnblogs.com/zhenghaotian/p/6723180.html
Copyright © 2011-2022 走看看