左偏树:一种可并堆
外节点:左子树为空或右子树为空
dist(i)指i到其后代中最近的外节点经过的边数
性质:
①节点键值小于等于它左右子节点键值
②节点左子节点距离不小于右子节点
——>左偏树的左右子树都是左偏树
——>③节点距离等于它右子节点距离+1(空节点dist为-1)
——>④若一颗左偏树距离为k,则其至少有2k+1-1个节点(完全二叉树)
——>⑤一颗N个节点的左偏树距离最多为floor(log(N+1))-1;
合并(Merge)(A,B——>C)
①判断是否交换,使A的根节点<=B的根节点
②把A的根节点设为新树的节点
③Merge(right(A),B)
④若right(A)距离比left(A)大,交换left(A)和right(A),并更新A的距离
插入(Insert(A,x))
①建立单节点x新树B
②Merge(A,B)
删除最小节点
①取出根节点
②合并左右子树
构建
算法一:逐点插入,nlogn
算法二
①讲n个节点放入先进先出队列
②从队首取出两颗左偏树,合并后加入队尾
③重复上述操作直至队列中只剩一棵树
O(n)
删除任意已知节点(从外部有映射指向)
①找到该节点,删除,Merge其左右子树,maintain该节点
maintain:
若当前节点为根节点或当前节点dist+1=父节点,操作结束,否则
1.若树为左子树,若其距离+1<父节点距离,则父节点距离=当前节点距离+1,再调整左右子树,maintain父节点;若其距离+1>父节点距离,跳出
2.若树为右子树,若其距离<=左子树,父节点距离=当前节点距离+1,maintain父节点;若其距离>左子树,交换左右子树,维护父节点距离,若父节点距离不变,则跳出,否则maintain父节点
O(logn)
左偏树基本操作
#include<cstdio> using namespace std; #define rep(i,a,b) for(int i=a;i<=b;++i) const int MAXN=200000+5; const int NUL=-1; int tot; struct node{ int l,r,fa,dist,key; }tr[MAXN]; inline void sw(int &a,int &b) {int t=a;a=b;b=t;return;} int build(int x) { ++tot; tr[tot].l=tr[tot].r=tr[tot].fa=NUL; tr[tot].dist=0;tr[tot].key=x; return tot; } int Merge(int a,int b) { if (a==NUL) return b; if (b==NUL) return a; int t; if (tr[a].key>tr[b].key) sw(a,b); tr[a].r=Merge(tr[a].r,b); if (tr[tr[a].r].dist>tr[tr[a].l].dist) sw(tr[a].r,tr[a].l); tr[a].dist=tr[tr[a].r].dist+1; return a; } int Insert(int a,int x) { x=build(x); return Merge(a,x); } inline int pop(int a) {return Merge(tr[a].l,tr[a].r);} inline int init1(int* s,int l,int r) { int x=build(s[l]); rep(i,l+1,r) x=Insert(x,s[i]); return x; } int q[4*MAXN]; inline int init2(int* s,int l,int r) { int h,t; h=t=0; rep(i,l,r) q[++t]=build(s[i]); int x,y; while (h+1<t) { x=q[++h];y=q[++h]; q[++t]=Merge(x,y); } return q[t]; } int main() { return 0; }
左偏树+并查集(每个并查集的最终返回值为所在左偏树的根)
#include<cstdio> using namespace std; #define rep(i,a,b) for(int i=a;i<=b;++i) const int MAXN=200000+5; const int NUL=-1; int fa[MAXN]; int tot; struct node{ int l,r,dist,key; }tr[MAXN]; inline void sw(int &a,int &b) {int t=a;a=b;b=t;return;} void init() { tot=0; } int getfather(int x) { if (fa[x]==x) return x; else return fa[x]=getfather(fa[x]); } int build(int x) { ++tot; tr[tot].l=tr[tot].r=NUL; tr[tot].dist=0;tr[tot].key=x; fa[tot]=tot; return tot; } int Merge(int a,int b) { if (a==NUL) return b; if (b==NUL) return a; int t; if (tr[a].key<tr[b].key) sw(a,b); tr[a].r=Merge(tr[a].r,b); if (tr[tr[a].r].dist>tr[tr[a].l].dist) sw(tr[a].r,tr[a].l); tr[a].dist=tr[tr[a].r].dist+1; fa[b]=a; return a; } int Insert(int a,int x) { x=build(x); return Merge(a,x); } inline int pop(int a) { int x=Merge(tr[a].l,tr[a].r);fa[x]=x;return x; }int main() { return 0; }
模板题hdu1512
#include<cstdio> using namespace std; #define rep(i,a,b) for(int i=a;i<=b;++i) const int MAXN=100000+5; const int NUL=-1; int fa[MAXN]; int tot; struct node{ int l,r,dist,key; }tr[MAXN]; inline void sw(int &a,int &b) {int t=a;a=b;b=t;return;} void init() { tot=0; } int getfather(int x) { if (fa[x]==x) return x; else return fa[x]=getfather(fa[x]); } int build(int x) { ++tot; tr[tot].l=tr[tot].r=NUL; tr[tot].dist=0;tr[tot].key=x; fa[tot]=tot; return tot; } int Merge(int a,int b) { if (a==NUL) return b; if (b==NUL) return a; int t; if (tr[a].key<tr[b].key) sw(a,b); tr[a].r=Merge(tr[a].r,b); if (tr[tr[a].r].dist>tr[tr[a].l].dist) sw(tr[a].r,tr[a].l); tr[a].dist=tr[tr[a].r].dist+1; fa[b]=a; return a; } int Insert(int a,int x) { x=build(x); return Merge(a,x); } inline int pop(int a) { int x=Merge(tr[a].l,tr[a].r);fa[x]=x; tr[a].l=tr[a].r=NUL;fa[a]=a;tr[a].dist=0; return x; } int id[MAXN],x,n,m,y,temp,s; int main() { while(~scanf("%d",&n)) { init(); rep(i,1,n) { scanf("%d",&x); build(x); } scanf("%d",&m); rep(i,1,m) { scanf("%d%d",&x,&y); //rep(i,1,n) printf("%d ",fa[i]); //printf(" "); //rep(i,1,n) printf("%d ",tr[i].r); //printf(" "); x=getfather(x);y=getfather(y); if (x==y) { printf("-1 "); continue; } else { x=Merge(x,y); printf("%d ",tr[x].key/2); tr[x].key/=2; Merge(pop(x),x); } } } return 0; }