【moban】左偏树(或斜堆)及配对堆模板

其实是几个算法思想都很简单的堆数据结构(配对堆待填坑) 始终使得左边一棵树下来很长然后每次插入插到右子树去,当发现右子树更长就swap(左,右),合并的时候例如小根堆,就将小的保留,大的与他的右儿子合并,并且设为小的的右儿子。 只有一个核心操作,加入就将他与root合并,删除将root删除,合并左右树。 啥你想学斜堆?去点dist,merge该咋咋,每次不根据dist来进行swap,直接每次都swap就可以了,,常数更小,实测更优!棒棒棒!(雾,时间复杂度log n 是正确的.这就代码实现更简单的 左偏树:
int merge(int a,int b)
{
    if(!a||!b) return a+b;
    if(dat[a]<dat[b]) swap(a,b);
    rs[a]=merge(rs[a],b);
    fa[rs[a]]=a;
    if(dist[ls[a]]<dist[rs[a]]) swap(ls[a],rs[a]);
    if(!rs[a]) dist[a]=0;
    else dist[a]=dist[rs[a]]+1;
    return a;
}
斜堆:(去TM的左偏树,斜堆大法吼!)
int merge(int a,int b)
{
    if(!a||!b) return a+b;
    if(dat[a]<dat[b]) swap(a,b);
    rs[a]=merge(rs[a],b);
    swap(ls[a],rs[a]);
    return a;
}
  配对堆: 插入:O(1),删除O(logn)合并O(1)取出最值O(1) 总觉得这个东西的插入合并O(1)在某些需要疯狂插入而很少删除的题目里会有用.这个堆不保证是二叉堆。 合并:我们考虑对两个堆合并,只用比较堆顶,(现在考虑大根堆)直接把小的那个插到大的那个下面当儿子。O(1) 插入:就是单独开个点,然后两个堆合并 删除:这东西最慢的操作O(logn),我们类同边分治重构树一样,提出他的所有儿子,然后两两合并一下,完成后就完成了删除操作。
struct stk{
    int sta[maxn],owo,lj;
    int gg(){ return lj?sta[lj--]:++owo; }
    void ins(int x) { sta[++lj]=x; }
}node,edge;
struct pdhp{
    int ss[maxn],st,sz=0,fa[maxn],la[maxn],nt[maxn],en[maxn],val[maxn],rt;
    void adg(int x,int y) {
        int owo = edge.gg(); en[owo]=y; nt[owo]=la[x]; la[x]=owo;
    }
    int merge(int x,int y) {
        if(val[x]>val[y]) swap(x,y); adg(fa[y]=x,y); return x;
    }
    int psh(int x) { int o=node.gg(); val[o]=x;rt=rt?merge(rt,o):o; }
    int top() { return val[rt]; }
    void pop() {
        st=0; for(int it=la[rt];it;it=nt[it]){
            edge.ins(it);
            if(fa[en[it]]==rt) fa[ss[++st] = en[it]]=0;
        }
        fa[rt] = la[rt] = 0; node.ins(rt); rt = 0;
        int p=0;
        while(p<st) {
            ++p; if(p==st) { rt = ss[p]; return; }
            int x = ss[p]; int y = ss[++p];
            ss[++st] = merge(x,y);
        }
    }
}hp;
 
posted @ 2018-12-12 16:43  Newuser233  阅读(9)  评论(0)    收藏  举报