讓我們對這棵樹進行肢解吧 樹鏈剖分

2022-07-19 22:57:30 字數 3908 閱讀 4219

樹鏈剖分,顧名思義,它先通過輕重邊剖分將樹分為多條鏈,保證每個點屬於且只屬於一條鏈,然後再通過資料結構(樹狀陣列、bst、splay、線段樹等)來維護每一條鏈。

這裡我用的是線段樹來維護,感覺應該算是最簡單的,但這還是花了我一段時間去理解。//我覺得樹鏈剖分講解好的部落格(

模板題:

樹鏈剖分,我覺得較為難的點有兩個,乙個是如何通過遍歷這棵樹得到樹的重鏈和輕鏈,另乙個是如何用線段樹來維護鏈。

通過這道例題,我們來探尋其奧秘。

如何通過遍歷這棵樹得到樹的重鏈和輕鏈?

首先來第一遍dfs,遍歷這顆樹,得到一些基本的東西,比如這個節點的父節點是誰 f [ ]以x為根節點的子樹內所有節點的總數 size[ ]這個節點的在樹裡面的深度 d [ ]以及   記錄當前結點的子節點   裡面擁有最多子節點數   的那個子節點 son[ ]。

如圖所示

我們可見,樹上的邊有些是加粗的邊,有些是沒有加粗的邊。加粗的邊連起來每乙個節點,我們叫做重鏈;反之,我們叫做輕鏈。

你能看出來是怎麼找出來重鏈輕鏈的嗎?如果當前點有很多個子節點,我們僅需看子節點下面有多少個節點,找出最多的那個,然後與這個子節點相連的邊就叫重邊,一直找下去,可得到樹裡面所以的重邊,然後形成重鏈。

比如我們看圖上的 1號節點,他子節點有3個,我們發現 4號節點下面的節點數最多,於是 1 和 4 之間的邊就叫重邊;

4 號節點,他子節點有3個,我們發現 9號節點下面的節點數最多,於是 4 和 9 之間的邊就叫重邊。

如果出現像 6 號節點這種情況,他有兩個子節點,但是子節點下面的節點數都為0,也就是下面的節點數相等,那麼我們可隨便找一條邊作為重邊。

然後做第二遍dfs,這次我們要把重鏈上的節點都標記乙個共同祖先(深度最低的)top [ ],然後通過優先走重鏈,再走輕鏈的方法,給每個節點標記上類似於時間戳的值 id [ ],rk陣列表示當前時間戳代表的哪個節點。

top搞出來有什麼用呢?怎麼那麼像並查集那樣的? 其實,top搞出來和後面的線段樹操作有關,也是難點。

id又有什麼用?我們可以聯想一下,為什麼並查集每次做完之後,都要把節點的father都改為乙個共同祖先?原因就是為了加速,我們在查詢兩個點之間的關係時,如果不在一條重鏈上,我們可以直接把當前點跳到祖先那裡,然後再看兩者的關係,這是後面要說到的,id還有另乙個妙用。

如何用線段樹來維護鏈?

比如例題裡面要求我們將樹從x到y結點最短路徑上所有節點的值都加上z。

分兩種情況,

一 在同一條重鏈上面,

那就好辦,我們再次看上圖,你會發現重鏈上的id值都是連續的,這說明了我們可以用線段樹來維護區間值,這個好理解。

二 不在同一條重鏈上面,那麼我們要怎麼做呢?

我們來看id值,剛剛講到,我們在移動點的時候,可直接把當前點跳到他的共同祖先那裡,跳的這個過程不能忽略,要用線段樹維護,這時候維護的是乙個區間(關係到》=2個點)。

但是這只適用於當前點在一條重鏈上面,如果不在重鏈上怎麼辦?那麼我們只能一步一步的走,走的這個過程不能忽略,要用線段樹維護,這時候維護的是乙個點(只關係到1個點)

最終有兩種情況了

1 我們把點都移到了同一條重鏈上面,如何判斷?看id值兩者是否相等。相等說明就在同一條重鏈上面,那麼之後處理如第一種情況

2 我們把點移到了一條輕鏈上面。我們只能通過一步一步走,走到一起。

可能我們現在還是有點懵逼,我用乙個**來表示(依據上面那個圖)

可看到重鏈基本上涉及兩個以上的區間,輕鏈在修改時只能類似去到乙個點上面去修改。

比如我要改8 到 14 節點的值,最終改的是線段樹區間裡面的(2,5)和(6,6)。在程式裡面操作不會直接(2,6)這麼修改。

其實就一句話,涉及到輕鏈上面的改動或查詢,一定是乙個乙個值的改,比如(6,6)、(7,7);而不是直接(6,7);而重鏈的話,可乙個乙個值改,也可一段一段改。

最後附上模板題**:

#include #define maxn 1000005

using

namespace

std;

struct

node

;node a[maxn];

intop,x,y,z,mod,n,m,r,p,i,first[maxn],dis[maxn],next[maxn],value[maxn],zhi[maxn],tot,size[maxn],id[maxn],f[maxn],depth[maxn],son[maxn],top[maxn],cnt,rank[maxn];

void add(int x,int

y)void dfs1(int

x) k=next[k];

}}void dfs2(int x,intt)}

void pushup(int

num)

void pushdown(int

num)

}void build(int l,int r,int

num)

int mid=(l+r)>>1

; build (l,mid,num*2

), build (mid+1,r,num*2+1

); a[num].l=a[num*2

].l;

a[num].r=a[num*2+1

].r;

pushup(num);

}void upgrade_3(int l,int r,int num,int

value)

pushdown(num);

int mid=(a[num].l+a[num].r)/2

;

if (mid>=l) upgrade_3(l,r,num*2

,value);

if (mid2+1

,value);

pushup(num);

}void upgrade_1(int x,int y,int

value)

if (id[x]>id[y]) swap(x,y);

upgrade_3(id[x],id[y],

1,value);

}int query(int l,int r,int

num)

int sum(int x,int

y)

if (id[x]>id[y]) swap(x,y);

return (ans+query(id[x],id[y],1))%mod;

}int

main()

cnt=0

,dfs1(r),dfs2(r,r);

build(

1,n,1

);

for (i=1;i<=m;i++)

}return0;

}

view code

讓我們都建立自己的知識樹吧

要學的東西很多,而我們的精力總是有限。你是否也有這樣的感慨呢?學這個沒有用,公司專案中用不到。你是否也有這樣的心態呢?這兩種理由本身沒有太大影響,甚至有時候還能幫你做判斷。但是你是否把這兩個作為藉口來拒絕所有新東西呢?我們要建立起自己的知識體系 知識樹 凡是有助於強化或者豐富這個知識體系的,都是有用...

01Trie樹 p2420 讓我們異或吧

異或是一種神奇的運算,大部分人把它總結成不進製加法.在生活中 xor運算也很常見。比如,對於乙個問題的回答,是為1,否為0.那麼 a是否是男生 xor b是否是男生 a和b是否能夠成為情侶 好了,現在我們來製造和處理一些複雜的情況。比如我們將給出一顆樹,它很高興自己有n個結點。樹的每條邊上有乙個權值...

洛谷P2420 讓我們異或吧(樹鏈剖分)

題目描述 異或是一種神奇的運算,大部分人把它總結成不進製加法.在生活中 xor運算也很常見。比如,對於乙個問題的回答,是為1,否為0.那麼 a是否是男生 xor b是否是男生 a和b是否能夠成為情侶 好了,現在我們來製造和處理一些複雜的情況。比如我們將給出一顆樹,它很高興自己有n個結點。樹的每條邊上...