可並堆講解


可並堆講解

  注:本篇博客以大根堆為例。

  可並堆,顧名思義,就是可以合並的堆。堆滿足一個性質,就是當前節點,都大於或者等於他的所有子樹上的節點,自然在這里我所講的是結點的權值。顯而易見,既然可並堆是堆的一種,容易推出,可並堆也滿足這個性質。

  現在思考一個問題,當題目里需要合並兩個堆的時候,該如何合並呢?如果只是普通的堆的話,我們可以運用啟發式合並的思想,每一次把size小的堆的點暴力插入到size大的堆里面,這個方法十分不優秀,時間復雜度是O(nlog2n)。所以我們會用可並堆來實現這個問題。

  可並堆是一種運用到左偏樹思想的堆,何為左偏樹?左偏樹顧名思義就是偏向左面的樹,如左圖就是一棵左偏樹,這棵樹明顯滿足一個性質。我們定義dis[p]為從p節點出發可以向右走的最大步數,從右圖中可以看到,當前點的左兒子的dis值都比右兒子的dis值大,只要滿足這個性質就是左偏樹。

  學過左偏樹之后,我們就可以學習可並堆了,可並堆的難點就是合並,那么應該如何合並呢?為了能保證時間復雜度,我們每一次合並都應該把新的節點放在當前的節點的右兒子上,這個是一個遞歸的過程,每一次把當前的兩個節點進行比較,留下權值大的點,然后遞歸下去把另一個點和留下的點的右兒子進行比較,如此下去,進行合並。當然每一次回溯之前,我們都需要判斷當前節點的左右兩個兒子的dis值,如果右兒子的dis值大於左兒子的dis值,則交換左右兒子。

int merge(int x,int y)
{
    if(!x) return y;
    if(!y) return x;
    if(num[x]<num[y]) swap(x,y);
    son[x][1]=merge(son[x][1],y);
    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;
}
//son[p][0]表示p號節點的左兒子
//son[p][1]表示p號節點的右兒子
//num[p]表示p號節點的權值

  下面就是刪除節點(最大值),只需要把他的兩個兒子合並就好啦,是不是很簡單?代碼就不附了,具體題目具體分析。

  大致就是這樣,不會的可以評論發問題,我會解答。

題目(我會不斷更新)

  bzoj1455&&luogu2713羅馬游戲:http://www.cnblogs.com/yangsongyi/p/8893005.html

  APIO2012派遣:http://www.cnblogs.com/yangsongyi/p/8921448.html

  JLOI2015城池攻占:http://www.cnblogs.com/yangsongyi/p/9046913.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM