zoukankan      html  css  js  c++  java
  • 左偏树(可并堆)

    模板:洛谷P3377 左偏树(可并堆)

    可并堆嘛,顾名思义,就是它可以实现合并两个堆仅消耗$O(logn)$的时间复杂度。而普通的堆要消耗$O(n)$或$O(nlogn)$。

    左偏树是一种优秀的可并堆,它同时具有堆和左偏的性质。其中左偏指从当前节点的左儿子出发到最近的一个“出口”的距离比从右儿子出发要长(或者相等)。

    “出口”,就是可以到达的一颗空树啦,如果一个节点没有或只有一个儿子,那它就是一个“出口”。

    为此我们为每个的维护一个离最近出口的距离值dis

    显然,由于左偏性,一点的$dis$等于它右儿子的$dis+1$($tr[x].dis=tr[tr[x].rc].dis+1$)

    左偏树最重要的操作就是merge,其用递归的方式实现合并。

    假设我们要合并两个堆$X$,$Y$,节点$x$,$y$是他们的根节点,试图把$x$的右子树和$y$合并后,把这颗合并后的新堆作为$x$的右儿子。但是需要满足(小根)堆的性质,所以先保证$x$的权值小于$y$的权值。然后维护左偏性质,如果左子树的$dis$比右子树的小,那么两个儿子交换。最后上传$dis$给$x$

    维护左偏性质可使合并的复杂度为$O(logn)$。

    可以知道,合并的时间复杂度取决于$dis$(因为如果有“出口”,Merge()就直接返回单节点了)。因为合并时我们都选择右儿子与另一个堆进行合并,根据左偏性得知,左儿子的$dis$比右儿子的$dis$大。那么可以使用与启发式合并类似的证明方法得知,向下递归的次数最多不超过$O(logn)$次。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    #include<queue>
    #include<ctime>
    #include<cstdlib>
    using namespace std;
    const int MXN=100005;
    
    namespace LTr{//Leftist Tree,左偏树 
        struct Node{
            int fa,lc,rc,able;
            int dis,val;
        }tr[MXN];
        int n;
        void Create(int x,int val){tr[x]=(Node){0,0,0,1,0,val};}//新建节点 
        int Root(int x){//找根,但这种方式很暴力,可能被构造O(n)T掉,所以保险建议用并查集 
            if(tr[x].fa) return Root(tr[x].fa);
            else return x;
        }
        int Comb(int x,int y){//合并 
            if(!x||!y) return x+y;
            if(tr[x].val>tr[y].val//保证小根堆
                ||(tr[x].val==tr[y].val&&x>y)) swap(x,y);
            tr[x].rc=Comb(tr[x].rc,y);//递归合并 
            if(tr[x].rc) tr[tr[x].rc].fa=x;
            if(tr[tr[x].lc].dis<tr[tr[x].rc].dis)//保证左偏 
                swap(tr[x].lc,tr[x].rc);
            tr[x].dis=tr[tr[x].rc].dis+1;//更新dis 
            return x;
        }
        //For User
        void Init(int myn){n=myn;Create(0,0);}
        bool IsAble(int x){return tr[x].able;}//是否已被删除 
        void Merge(int x,int y){Comb(Root(x),Root(y));}
        int Top(int x){return tr[Root(x)].val;}//返回x所在堆的堆顶值 
        void Pop(int x){//弹出x所在堆的堆顶 
            x=Root(x);
            int lc=tr[x].lc,rc=tr[x].rc;
            tr[lc].fa=tr[rc].fa=0;//更新其儿子的父节点为空 
            tr[x]=(Node){0,0,0,0,0,0};//标记该点已经不存在 
            Merge(lc,rc);//合并左右节点 
        }
    }
    int n,qn;
    int main(){
        cin>>n>>qn;
        LTr::Init(n);
        for(int i=1;i<=n;i++){
            int val;scanf("%d",&val);
            LTr::Create(i,val);
        }
        for(int i=1;i<=qn;i++){
            int type,x,y;scanf("%d",&type);
            switch(type){
                case 1:{
                    scanf("%d%d",&x,&y);
                    if(LTr::IsAble(x)&&LTr::IsAble(y)&&LTr::Root(x)!=LTr::Root(y))
                        LTr::Merge(x,y);
                break;}
                case 2:{
                    scanf("%d",&x);
                    if(LTr::IsAble(x)){
                        printf("%d
    ",LTr::Top(x));
                        LTr::Pop(x);
                    }else printf("-1
    ");
                break;}
            }
        }
        return 0;
    }
    View Code(会被卡)
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    #include<queue>
    #include<ctime>
    #include<cstdlib>
    using namespace std;
    const int MXN=100005;
    
    namespace LTr{//Leftist Tree,左偏树 
        struct Node{
            int jmp,lc,rc,able;
            int dis,val;
        }tr[MXN];
        int n;
        void Create(int x,int val){tr[x]=(Node){x,0,0,1,0,val};}//新建节点 
        int Root(int x){//并查集维护jmp找根
            if(tr[x].jmp==x) return tr[x].jmp;
            return tr[x].jmp=Root(tr[x].jmp);
        }
        int Comb(int x,int y){//合并 
            if(!x||!y) return x+y;
            if(tr[x].val>tr[y].val//保证小根堆
                ||(tr[x].val==tr[y].val&&x>y)) swap(x,y);
            tr[x].rc=Comb(tr[x].rc,y);//递归合并 
            if(tr[x].rc) tr[tr[x].rc].jmp=x; 
            if(tr[tr[x].lc].dis<tr[tr[x].rc].dis)//保证左偏 
                swap(tr[x].lc,tr[x].rc);
            tr[x].dis=tr[tr[x].rc].dis+1;//更新dis 
            return x;
        }
        //For User
        void Init(int myn){n=myn;Create(0,0);}
        bool IsAble(int x){return tr[x].able;}//是否已被删除 
        void Merge(int x,int y){Comb(Root(x),Root(y));}
        int Top(int x){return tr[Root(x)].val;}//返回x所在堆的堆顶值 
        void Pop(int x){//弹出x所在堆的堆顶 
            x=Root(x);
            int lc=tr[x].lc,rc=tr[x].rc;
            tr[lc].jmp=lc;tr[rc].jmp=rc;//一定要加,否则会在被删除的点和新根中反复横跳 
            tr[x]=(Node){Comb(lc,rc),0,0,0,0,0};
            //合并(注意用的Comb()而不是Merge())左右儿子,标记该点已经不存在,同时把jmp改到合并后的根上 
        }
    }
    int n,qn;
    int main(){
        cin>>n>>qn;
        LTr::Init(n);
        for(int i=1;i<=n;i++){
            int val;scanf("%d",&val);
            LTr::Create(i,val);
        }
        for(int i=1;i<=qn;i++){
            int type,x,y;scanf("%d",&type);
            switch(type){
                case 1:{
                    scanf("%d%d",&x,&y);
                    if(LTr::IsAble(x)&&LTr::IsAble(y)&&LTr::Root(x)!=LTr::Root(y))
                        LTr::Merge(x,y);
                break;}
                case 2:{
                    scanf("%d",&x);
                    if(LTr::IsAble(x)){
                        printf("%d
    ",LTr::Top(x));
                        LTr::Pop(x);
                    }else printf("-1
    ");
                break;}
            }
        }
        return 0;
    }
    View Code(并查集)
  • 相关阅读:
    How to convert VirtualBox vdi to KVM qcow2
    (OK)(OK) adb -s emulator-5554 shell
    (OK)(OK) using adb with a NAT'ed VM
    (OK) How to access a NAT guest from host with VirtualBox
    (OK) Creating manually one VMs from an existing VDI file in CLI (VBoxManage) in Fedora 23
    (OK)(OK) Creating VMs from an existing VDI file in CLI (VBoxManage) in Fedora 23
    (OK) Creating_VMs_from_an_existing_VDI_file.txt
    (OK) Creating VMs from an existing VDI file —— in OS X
    (OK) install_IBM_SERVER.txt
    (OK) install chrome & busybox in android-x86_64 —— uninstall chrome
  • 原文地址:https://www.cnblogs.com/sun123zxy/p/leftisttree.html
Copyright © 2011-2022 走看看