樹鏈剖分詳解 後期會不斷更新

2021-08-21 21:54:23 字數 2609 閱讀 8156

1.dfs;

2.線段樹。

相信dfs大家都會,估計只有線段樹了。

如果有不會的請點這裡:線段樹系列文章(未完)

就是將一棵樹分成許多條鏈,使得樹中所有節點都被包含在這些鏈裡。

(換句話說:就是一種使你的**瞬間增加1kb的演算法。)

#怎麼剖分?

1.隨便剖分

隨便找乙個節點,將它作為鏈頭向下剖分。

2.隨機剖分

隨便剖分+一點特判。

3.輕重鏈剖分

將重邊(後面會講)連成鏈。

顯然,對於隨機資料,三種方法沒什麼區別。

但是對於第

一、二種方法,若使用刻意構造的資料去測試,則會被卡掉。

綜上所述,為了保險起見,也為了演算法穩定性,我們選擇輕重鏈剖分。

重兒子:對於每個節點的兒子,若該兒子所在的子樹的大小是所有兒子中最大的,則稱該兒子為重兒子。每個節點有且只有乙個重兒子。

輕兒子:除了重兒子的節點。

重邊:連線重兒子與其父親節點的邊。

輕邊:連線輕兒子與其父親節點的邊。

重鏈:由重邊連線而成的鏈。注意鏈內有且只有重邊。

相信你也看不懂(hhh),舉個例子吧:

如圖,在這棵樹中:紅框框著的點就是重兒子,紫色邊表示重邊,黑色邊表示輕邊。可以看出兩條重鏈:和。而且,我們發現,重鏈的起點都是輕兒子。

然後,我們可以對所有節點進行重編號(優先重兒子),就會得到如下的結果:(藍色點即為新標號)

我們發現:重鏈中,標號都是連續的。於是,我們可以想象一下:將所有重鏈按鏈首標號進行排序,並拉直平放起來;我們就得到了乙個線性的序列。

並且:我們可以發現一些性質:

性質一:對於兩個節點u,v,若v是u的輕兒子,則有:siz[v]<=siz[u]/2,若v是u的重兒子,則:siz[v]>siz[u]/2;

性質二:對於乙個任意節點u,它到根節點的路徑最多有log⁡2

n\log_2 n

log2​n

條輕邊,log⁡2

n\log_2n

log2​n

條重路徑。

接下來就很玄學了

由上一步可知,我們得到了乙個序列,於是,我們就可以把這一堆東西扔給線段樹了。

我們可以進行權值修改、路徑查詢等操作,而做到這一切,都只需要o(n

log⁡2n

)o(n\log_2n)

o(nlog2​

n)的時間複雜度。

先扔來一堆陣列名字:

val[1...n]:記錄每個節點的權值(讀入時);

siz[1...n]:記錄以該節點為根的子樹的大小;

top[1...n]:記錄每個節點所在重鏈的鏈首;

son[1...n]:記錄每個節點的重兒子;

dep[1...n]:記錄該節點在樹中的深度;

tid[1...n]:記錄每個節點的新標號;

rnk[1...n]:記錄新標號所對應的節點,是tid的反函式;

fa[1...n]:記錄每個節點的父親節點。

暈了對吧?別慌,其實都非常有用的,理解了就非常容易。

這次dfs我們主要處理一些基礎資訊,即:sizsondepfa四個陣列。

這種事情應該不會很困難吧?直接粘**了。

void

dfs1

(int u,

int f,

int d)

}

struct tnode e[

2*maxn+5]

;tnode *g[maxn+5]

,*ecnt=

&e[0];

void

addedge

(int u,

int v,

int w)

這次dfs主要處理剩下的資訊:toptidrnk三個陣列。

我們在dfs傳入乙個引數tp,表示當前所在重鏈的鏈首,若遇到輕兒子則將tp設為該節點並繼續往下傳。

在處理tidrnk時,我們開乙個全域性變數dcnt,每次進dfs時加一,就可以處理出這些資訊了。**如下:

void

dfs2

(int u,

int tp)

}

做完這些後,我們就完成了整個樹鏈剖分的實現。

剩下的就是各種資料結構的事情了。

例題1:spoj qtree query on a tree

樹鏈剖分詳解

樹鏈剖分定義 只是把一棵樹拆成鏈來處理而已,即將樹上的某些段一起通過資料結構優化進行處理來降低複雜度。樹鏈剖分相關定義 重兒子 ve v 為 u 的子節點中ve 值最大的,那麼 v 就是 u的重兒子 將子樹中最長的那一條鏈一起處理來降低複雜度 輕兒子 u 除了重兒子的其它子節點。重邊 點 u與其重兒...

詳解樹鏈剖分

樹鏈剖分,顧名思義為將鏈剖開分成多條。當我們想要修改樹上一條路的值或求值時,我們暴力只能用乙個個修改,這是非常慢的。這時,我們就要想乙個辦法,資料結構?但是資料結構我們都需要連續修改,可是樹上路徑的編號是不連續的。於是我們想了乙個辦法。我們先定義 fa x 為x的父親 dep x 為x的深度 siz...

樹鏈剖分詳解

重兒子 對於每乙個非葉子節點,它的兒子中 以那個兒子為根的子樹節點數最大的兒子 為該節點的重兒子 ps 感謝 shzr大佬指出我此句話的表達不嚴謹qwq,已修改 輕兒子 對於每乙個非葉子節點,它的兒子中 非重兒子 的剩下所有兒子即為輕兒子 葉子節點沒有重兒子也沒有輕兒子 因為它沒有兒子。重邊 乙個父...