• 并不对劲的斜堆


    为了反驳隔壁很对劲的太刀流,并不对劲的片手流将与之针锋相对。

    很对劲的斜堆、左偏树简明教程->

    它们是可并堆的两种实现方式。

    (假装二叉堆只包括小根堆。)

    二叉堆该如何合并?先想一种暴力的。

    现在有根的键值较小的二叉堆A,键值较大的二叉堆B。

    在合并后,A的根肯定还是根。若A的左、右子树都不为空的话,则可以随便选一个,再将这个堆与B合并。

    递归地重复这个过程,直到左、右子树中有一方为空,直接接过去就行了。

    然而这样时间复杂度并不会得到保障,因为每次“随便选”的那一个可能更深。这样时间复杂度就玄学了。

    斜堆则是在暴力的基础上有一些修改。每次必选右(或左)子树合并,然后交换左右子树。这样下次就轮到这次没能合并的子树与之合并了。

    这听上去和暴力区别不大,还有那么一些些的扯。但是类似于splay,它的时间复杂度均摊却是O(log n)的。至于证明,类似于splay,并不对劲的人并不知道,就感性理解吧。

    加点操作相当于与一个只有一个节点的斜堆合并。

    删除相当于去掉堆顶元素,再将它的左、右子树合并。

    可以去洛谷p3377测评,斜堆好像也能过。

    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define maxn 100010
    using namespace std;
    int read()
    {
        int f=1,x=0;char ch=getchar();
        while(isdigit(ch)==0 && ch!='-')ch=getchar();
        if(ch=='-')f=-1,ch=getchar();
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    void write(int x)
    {
        int ff=0;char ch[15];
        if(x<0)
        {
            x=-x;
            putchar('-');
        }
        while(x)ch[++ff]=(x%10)+'0',x/=10;
        if(ff==0)putchar('0');
        while(ff)putchar(ch[ff--]);
        putchar('
    ');
    }
    struct node
    {
        int key,ls,rs,dis;
    }xx[maxn];
    int fa[maxn],n,q;
    int x,y,l,r,tx,ty; 
    bool cmp(int _1,int _2)//Smaller.
    {
        return xx[_1].key==xx[_2].key?_1<_2:xx[_1].key<xx[_2].key;
    }
    int f(int x){return fa[x]<0?x:f(fa[x]);} 
    int merge(int A,int B) 
    {
        if(!A || !B)return A+B;
        if(!cmp(A,B))swap(A,B);
        xx[A].rs=merge(xx[A].rs,B);
        fa[xx[A].rs]=A;
        swap(xx[A].ls,xx[A].rs);
        return A;
    }
    void getit()
    {
        x=read(),y=read();
        if(xx[x].key<0 || xx[y].key<0)return;
        tx=f(x),ty=f(y);
        if(tx==ty)return;
        merge(tx,ty);
    }
    void delmin(int u)
    {
        l=xx[u].ls,r=xx[u].rs;
        xx[u].key=fa[l]=fa[r]=-1;
        merge(l,r);
    }
    void ask()
    {
    //    printfa();
        x=read();
        if(xx[x].key==-1){write(-1);return;}
        tx=f(x);
        write(xx[tx].key);
        delmin(tx);
    //    printfa();
    }
    void work()
    {
        n=read(),q=read();
        xx[0].dis=-1;
        memset(fa,-1,sizeof(fa));
        for(int i=1;i<=n;i++)
            xx[i].key=read();
        int f;
        while(q--)
        {
            f=read();
            if(f==1)
                getit();
            else 
                ask();
        }
    }
    int main()
    {
        work();
        return 0;
    }
    /*
    5 5
    1 5 4 2 3
    1 1 5
    1 2 5
    2 2
    1 4 2
    2 2
    */
    并不对劲的斜堆

     好像写得比简明教程还简明呢。

  • 相关阅读:
    Nova创建虚拟机的底层代码分析
    Chapter 1 Securing Your Server and Network(12):保护链接server
    c语言实现hashtable,相似C++的map和iOS的NSDictionary
    Android_Training
    Bean Validation 技术规范特性概述
    linux下串口调试工具/串口终端推荐: picocom
    Android:主题(Theme)
    oracle database 12c R1 安装文档
    python版本wifi共享工具
    iOS 6编程Cookbook(影印版)
  • 原文地址:https://www.cnblogs.com/xzyf/p/8378223.html
Copyright © 2020-2023  润新知