1. 程式人生 > >並不對勁的左偏樹

並不對勁的左偏樹

getchar() nod code hid 代碼 兩種 fin mes using

為了反駁隔壁很對勁的太刀流,並不對勁的片手流將與之針鋒相對。

很對勁的斜堆、左偏樹簡明教程

它們是可並堆的兩種實現方式。

(還是假裝二叉堆只包括小根堆。)

斜堆的缺點在於,每次合並的堆大小不同,無條件交換左右子樹可能遇到某些坑孫子的數據,復雜度會變得玄學。

左偏樹是在斜堆上有所改進的。根據斜堆的代碼,可以註意到合並的時間復雜度是第一個至多一個子樹的點到根的距離。

把它稱為dis,那麽當右子樹的dis大於左子樹的dis時才有必要交換。

好像沒那麽玄學了呢。

需要註意的是,至多一個子樹的點的dis為0,而空節點的dis為-1。

技術分享圖片
#include<iostream>
#include<iomanip>
#include
<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define maxn 100010 using namespace std; int read() { int f=1,x=0;char ch=getchar(); while(isdigit(ch)==0 && ch!=-)ch=getchar(); if(ch==-)f=-1,ch=getchar(); while(isdigit(ch))x=x*10
+ch-0,ch=getchar(); return x*f; } void write(int x) { int ff=0;char ch[15]; if(x<0) { x=-x; putchar(-); } while(x)ch[++ff]=(x%10)+0,x/=10; if(ff==0)putchar(0); while(ff)putchar(ch[ff--]); putchar(\n); } struct node { int key,ls,rs,dis; }xx[maxn];
int fa[maxn],n,q; int x,y,l,r,tx,ty; bool cmp(int _1,int _2)//Smaller. { return xx[_1].key==xx[_2].key?_1<_2:xx[_1].key<xx[_2].key; } int f(int x){return fa[x]<0?x:f(fa[x]);} int merge(int A,int B) { if(!A || !B)return A+B; if(!cmp(A,B))swap(A,B); xx[A].rs=merge(xx[A].rs,B); fa[xx[A].rs]=A; if(xx[xx[A].ls].dis<xx[xx[A].rs].dis) swap(xx[A].ls,xx[A].rs); xx[A].dis=xx[xx[A].rs].dis+1; return A; } void getit() { x=read(),y=read(); if(xx[x].key<0 || xx[y].key<0)return; tx=f(x),ty=f(y); if(tx==ty)return; merge(tx,ty); } void delmin(int u) { l=xx[u].ls,r=xx[u].rs; xx[u].key=fa[l]=fa[r]=-1; merge(l,r); } void ask() { // printfa(); x=read(); if(xx[x].key==-1){write(-1);return;} tx=f(x); write(xx[tx].key); delmin(tx); // printfa(); } void work() { n=read(),q=read(); xx[0].dis=-1; memset(fa,-1,sizeof(fa)); for(int i=1;i<=n;i++) xx[i].key=read(); int f; while(q--) { f=read(); if(f==1) getit(); else ask(); } } int main() { work(); return 0; } /* 5 5 1 5 4 2 3 1 1 5 1 2 5 2 2 1 4 2 2 2 */
並不對勁的左偏樹

好像寫得比簡明教程還簡明呢。

並不對勁的左偏樹