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