【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;

浙公网安备 33010602011771号