左偏树(可合并堆)(包括pbds库)

左偏树(可合并堆): 有时需要将两个堆合并,此时每次都对一个pop()再在另一个push() ,太慢了。此时直接合并堆会更快,设堆1有size1个节点,堆2有size2个节点,则合并堆1堆2的复杂度为: O ( l o g ( s i z e 1 ) ) + O ( l o g ( s i z e 2 ) ) O(log(size1))+O(log (size2)) O(log(size1))+O(log(size2))


例题:P3377 【模板】左偏树(可并堆)

手写左偏树版:
#include<bits/stdc++.h>
#define ll long long
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define DEBUG cout<<",here\n";
#define Rep(i,l,r) for(int i=(l);i<=(r);i++)
#define rep(i,l,r) for(int i=(l);i< (r);i++)
#define Rev(i,r,l) for(int i=(r);i>=(l);i--)
#define rev(i,r,l) for(int i=(r);i> (l);i--)
#define MAXN 100005
using namespace std;

struct L_Tree
{
    struct Node
    {    
        int val;
        int lc,rc,dis,par;
    };
    const static int maxn=MAXN;
    Node t[maxn];

    void init(int x){
        for (int i = 1; i <= x; i++) {
            t[i].lc = t[i].rc = t[i].dis = 0;
            t[i].val=-1;
            //并查集默认值0,不用特意修改
        }
        t[0].dis=-1;
    }

	//注意该并查集默认值为0
    int find(int x){if(t[x].par==0)return x;return t[x].par=find(t[x].par);}

    int join(int x, int y) {
        if(!x||!y) return x+y;
        if(t[x].val>t[y].val||(t[x].val==t[y].val&&x>y))//修改这里来适应大/小根堆
            swap(x,y);
        t[x].rc=join(t[x].rc,y);t[t[x].rc].par=x;
        if(t[t[x].lc].dis<t[t[x].rc].dis)
            swap(t[x].lc,t[x].rc);
        t[x].dis=t[t[x].rc].dis+1;
        return x;
    }
    void pop(int x){
        t[x].val=-1;
        t[t[x].lc].par=t[t[x].rc].par=0;
        t[x].par=join(t[x].lc,t[x].rc);
    }
};

int n,m;
L_Tree q;

int main()
{
    cin>>n>>m;
    q.init(n);
    Rep(i,1,n){
        int tmp;cin>>tmp;
        q.t[i].val=tmp;
    }
    Rep(i,1,m){
        int op;cin>>op;
        if(op==1){
            int x,y;cin>>x>>y;
            if(q.t[x].val==-1||q.t[y].val==-1)continue;//被删除了
            x=q.find(x),y=q.find(y);
            if(x==y)continue;//已经在一个堆中
            q.join(x,y);
        }
        else{
            int x;cin>>x;
            if(q.t[x].val==-1){cout<<"-1\n";continue;}//被删除了
            x=q.find(x);
            cout<<q.t[x].val<<"\n";
            q.pop(x);
        }
    }

    return 0;
}
pbds库版:

__gnu_pbds是gcc库里的,大多数用gcc编译器的oj都支持,用clang的加上后文代码前三行也行。

介绍:

定义:

#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef __gnu_pbds::priority_queue <pair<int, int>, greater<pair<int, int> >, pairing_heap_tag> heap;
heap q;

成员方法:

代码:

#ifdef __clang__
#define __builtin_sprintf sprintf
#endif
#include<bits/stdc++.h>
#include<bits/extc++.h>
#define ll long long
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define DEBUG cout<<",here\n";
#define Rep(i,l,r) for(int i=(l);i<=(r);i++)
#define rep(i,l,r) for(int i=(l);i< (r);i++)
#define Rev(i,r,l) for(int i=(r);i>=(l);i--)
#define rev(i,r,l) for(int i=(r);i> (l);i--)
using namespace std;

struct L_Tree
{
    int v,id;
    bool operator<(const L_Tree& bb)const{
        if(v==bb.v)return id>bb.id;
        return v>bb.v;
    }
};
__gnu_pbds::priority_queue<L_Tree>q[100005];

int n,m;
int f[100005];
map<int,int>del;

int find(int x){if(f[x]==x)return x; return f[x]=find(f[x]);}

int main()
{
    cin>>n>>m;
    Rep(i,1,n){
        int tmp;cin>>tmp;
        q[i].push({tmp,i});
        f[i]=i;
    }
    Rep(i,1,m){
        int op;cin>>op;
        if(op==1){
            int x,y;cin>>x>>y;
            if(del[x]||del[y])continue;
            int fx=find(x),fy=find(y);
            if(fx>fy)swap(fx,fy);
            if(fx!=fy){
                q[fx].join(q[fy]);//pbds库,合并堆
                f[fy]=fx;
            }
        }
        else{
            int x;cin>>x;
            if(del[x]==1){cout<<"-1\n";continue;}
            x=find(x);
            if(!q[x].empty()){
                del[q[x].top().id]=1;
                cout<<q[x].top().v<<"\n";
                q[x].pop();
            }
        }
    }

    return 0;
}

参考:
左偏树模板
左偏树模板
pb_ds优先队列学习笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值