如題,一開始有 nn 個小根堆,每個堆包含且僅包含乙個數。接下來需要支援兩種操作:
1 x y
:將第 xx 個數和第 yy 個數所在的小根堆合併(若第 xx 或第 yy 個數已經被刪除或第 xx 和第 yy 個數在用乙個堆內,則無視此操作)。
2 x
:輸出第 xx 個數所在的堆最小數,並將這個最小數刪除(若有多個最小數,優先刪除先輸入的;若第 xx 個數已經被刪除,則輸出 -1−1 並無視刪除操作)。
左偏樹byhsfzzh1
左偏樹是一種支援在o(logn*logn)的時間複雜度內進行合併的堆式資料結構
一些定義:
外節點:左兒子或右兒子是空節點的節點
距離:乙個節點的距離d[x]定義為其子樹中與節點x最近的外節點到x的距離,特別的,定義空節點的距離為-1。
左偏樹的基本性質:
具有堆的性質。
具有左偏性質,即對於每個節點x,有d[l]>d[r]。
基本結論:
(1)節點x的距離d(x) = d(rc) + 1
(2)距離為n的左偏樹至少有2^(n+1)-1個節點,此時該左偏樹的形態是一顆滿二叉樹。
(3)有n個節點的左偏樹的根節點的距離是o(logn)的。
合併操作:
左偏樹最基本的操作是合併操作。
定義merge(x,y)為合併兩顆分別以xy為根節點的左偏樹。其返回值為合併之後的根節點。
首先不考慮左偏性質,我們描述一下合併兩個具有堆性質的樹的過程。假設我們要合併的是小根堆。
(1)若vx<=vy,以x作為合併後的根節點,否則以y作為合併後的根節點。
(2)將y與x的其中乙個兒子合併,用合併後的根節點代替與y合併的兒子的位置,並返回x。
(3)重複以上操作。如果x和y中有乙個為空節點,返回x+y。
令h為樹高,每次合併都減少1,上述操作的時間複雜度是o(h)的。
當要合併的樹退化成一條鏈的時候,這樣做的複雜度是o(n)的。
要使時間複雜度更優,就要使樹合併得更平衡。我們有兩種方式:
(1)每次隨機選擇x的左右兒子進行合併。(很像treap)。
(2)左偏樹。
由於左偏樹中左兒子的距離大於右兒子的距離,我們每次將y與x的右兒子合併。
由於左偏樹的樹高是logn的,所以單次合併的時間複雜度也是logn的。
但是,兩顆左偏樹按照上述方法合併後,可能不再保持左偏樹的左偏性質,在每次合併完後,判斷對節點x是否有d(lc)>d(rc)。若沒有,則交換lc,rc,並維護x的距離d(x) = d(rc) + 1。
插入給定值:
新建乙個值等於插入值的節點,將該節點和左偏樹合併即可。時間複雜度o(logn)。
求最小值:
由於左偏樹的堆性質,左偏樹上的最小值為其根節點的值。
刪除最小值:
等價於刪除左偏樹的根節點,合併根節點的左右兒子即可。
#includeusingnamespace
std;
const
int maxn=1e5+100
;int
n,m,op,x,y;
intlc[maxn];
intrc[maxn];
intd[maxn];
intfather[maxn];
bool
tf[maxn];
struct
left_tree
}a[maxn];
int findfather (int
x)
returnx;}
int merge (int x,int
y) int
main ()
if (op==2
) x=findfather(x);
printf(
"%d\n
",a[x].v);
tf[x]=1
; father[lc[x]]=father[rc[x]]=father[x]=merge(lc[x],rc[x]);
lc[x]=rc[x]=d[x]=0
; }
}}
P3377 模板 左偏樹 可並堆
開始時n nn個只有乙個數的集合,要求支援 合併兩個集合 查詢乙個集合中的最小值並刪除 左偏樹就是維護乙個滿足以下性質的樹 對於v al xval x valx 有val x llsx val xva lx llsx 且v al x lrsx val xva lx lrsx 對於任何節點有左子樹的深...
P3377 模板 左偏樹(可並堆)
如題,一開始有n個小根堆,每個堆包含且僅包含乙個數。接下來需要支援兩種操作 操作1 1 x y 將第x個數和第y個數所在的小根堆合併 若第x或第y個數已經被刪除或第x和第y個數在用乙個堆內,則無視此操作 操作2 2 x 輸出第x個數所在的堆最小數,並將其刪除 若第x個數已經被刪除,則輸出 1並無視刪...
P3377 模板 左偏樹(可並堆)
如題,一開始有n個小根堆,每個堆包含且僅包含乙個數。接下來需要支援兩種操作 操作1 1 x y 將第x個數和第y個數所在的小根堆合併 若第x或第y個數已經被刪除或第x和第y個數在用乙個堆內,則無視此操作 操作2 2 x 輸出第x個數所在的堆最小數,並將其刪除 若第x個數已經被刪除,則輸出 1並無視刪...