標籤:inf || png main ora 大於 傾斜 its lan
最近學了左偏樹,學的時候深感網上沒有詳細教程之苦,所以自己來寫一篇(因為是蒟蒻所以可能寫的不是很好)
左偏樹是什嗎?
左偏,顧名思義,就是往左傾斜,左偏樹既滿足堆的性質,又滿足左偏的性質
因為它向左傾斜,所以可以有效減少查詢的時間複雜度
先來看看一顆左偏樹
這就是一顆左偏樹(雖然有點醜)
左偏樹有兩個重要的值:索引值,距離
索引值就是點的權值,而每個點的距離值就是它的右兒子的距離值加1
維護左偏這一性質靠的就是距離值,而維護堆的性質靠的就是權值
左偏樹至少滿足以下幾種操作
合并,查詢,刪除
合并:
先來說說合并;合并,就是把兩顆左偏樹合成一顆左偏樹
基本流程:
1.判斷哪顆左偏樹的堆頂更小,將大的合到小的中(依題目而定)
2.若被插入樹為空白,則直接插入,否則找到被插入樹的右子樹,繼續合并
3.插入完後,若右子樹的dis值大於左子樹,則交換左右子樹
所以,合并就是一個遞迴過程
查詢:
查詢一般是查詢樹的最小值,也就是堆頂元素(依情況而定),就不再多說了
刪除:
刪除一個點其實就是將這個點的左右子樹合并,再與原樹合并就行了
講了這麼多,接下來我們就練練手吧:洛穀左偏樹模板:https://www.luogu.org/problemnew/show/P3377
顯然,這道題是一道名副其實的模板題,只要掌握了我之前所講的左偏樹基本操作就能A
那麼,直接上代碼
#include<bits/stdc++.h>#define N 100001using namespace std;int n,m,fa[N],val[N],dis[N],son[N][2];int gf(int x){ while(fa[x])x=fa[x]; return x;}int merge(int x,int y){ if(x==0||y==0)return x+y; if(val[x]>val[y]||(val[x]==val[y]&&x>y))swap(x,y); son[x][1]=merge(son[x][1],y); fa[son[x][1]]=x; if(dis[son[x][1]]>dis[son[x][0]])swap(son[x][1],son[x][0]); dis[x]=dis[son[x][1]]+1; return x;}//合并void pop(int x){ val[x]=-1; fa[son[x][0]]=fa[son[x][1]]=0; merge(son[x][0],son[x][1]);}//刪除int main(){ scanf("%d%d",&n,&m); dis[0]=-1; for(int i=1;i<=n;i++)scanf("%d",&val[i]); for(int i=1;i<=m;i++){ int opt;scanf("%d",&opt); switch(opt){ case 1:{ int x,y;scanf("%d%d",&x,&y); x=gf(x),y=gf(y); if(val[x]==-1||val[y]==-1)continue; if(x==y)continue; merge(x,y); break; } case 2:{ int x;scanf("%d",&x);x=gf(x); if(val[x]==-1){ printf("%d\n",-1); continue; } printf("%d\n",val[x]); pop(x); break; } } } return 0;}
A了這道題,就說明你已經掌握了左偏樹這個牛B的資料結構了,所以說,
資料結構其實也沒有那麼難(!誤)
左偏樹教程