說到樹鏈剖分,其實故事還挺多的。我記得在高中的OI經歷中,我曾無數次聽到這個名詞,各種省賽、邀請賽貌似都會考這個東西,那時我覺得樹鏈剖分深不可測,是我等蒟蒻不能理解的東西……然后我還記得,某年(好像是NOip2014?)有一題貌似也要用樹鏈剖分??傊潜慌暗囊籅,然后現在覺得這個東西也沒什么難的……
好吧,言歸正傳。所謂樹鏈剖分其實就是樹的輕重鏈剖分,而樹鏈就是指從某一父節點一直到葉子節點的一條鏈。下面先介紹幾個概念:
1、重兒子:父節點的所有兒子中,子樹節點數目最多的兒子
2、重邊:連接父節點和兒子的邊
3、重鏈:從父節點往下所有重兒子連在一起的鏈
有了這些定義,我們就能顯而易見的發現:任何節點都只屬于一條樹鏈。然后按照這樣把一棵樹剖分成這樣一條一條的樹鏈,樹鏈剖分就完成了,如圖:
其中這棵樹被剖分成了紅、綠、藍三條鏈,但是要注意,圈出來的兩條邊其實是不屬于樹鏈的。是的完成了這個,樹鏈剖分就已經做完了。其實剖分很容易理解,但是問題是這樣做了有什么用呢?這個坑我們后面填~接下來先說說如何實現它。
第一步當然是對于每個非葉子節點都要選取一條重邊以及重兒子,這就是重鏈的延伸方向。如你所想,用dfs很容易實現,依次遍歷該節點的子樹,然后挑選可達深度最大的點作為重兒子,具體代碼如下:
inline void dfs1(int u, int f, int d) //u為當前節點,f為父親,d為當前深度{ dep[u] = d; //先記下深度 size[u] = 1; //size[]表示子樹的節點數目 son[u] = 0; //son[]表示重兒子 fa[u] = f; efor(i,u) //遍歷子樹 { int ff = g[i].y; if (ff == f) continue; dfs1(ff, u, d + 1); size[u] += size[ff]; if (size[son[u]] < size[ff]) son[u] = ff; //選取節點數最多的點作為重兒子 }}標記完重兒子后,我們要把每條鏈連起來,真正形成鏈。那么,我們用什么表示一條鏈呢?我們知道,樹狀數組、線段樹、平衡樹都是能支持在一段區間里維護性質,若我們能把一條鏈上的節點弄在一段連續的區間中,那我們就容易處理這些問題了。于是便想到了對每個節點進行重(chong)標號,一條鏈上的點的新編號一定是連續的,具體的還是利用dfs來實現,代碼:
inline void dfs2(int u, int tp) //tp表示當前樹鏈的鏈頭,其實這個操作應該叫label{ top[u] = tp; //top[]表示節點i所屬樹鏈的鏈頭,方便查詢和修改 id[u] = ++num; //id[]表示一個節點新的id(編號) Rank[id[u]]=u; //Rank[]表示新編號i對應原來的編號,即Rank[id[i]]=i if (son[u]) dfs2(son[u], tp); //先對同一條鏈上的重兒子標號 efor(i,u) { int ff = g[i].y; if (ff == fa[u] || ff == son[u]) continue; dfs2(ff, ff); //對其他兒子標號 }}這樣通過dfs1和dfs2,我們就完成了樹鏈剖分,現在開始填坑……
樹鏈剖分適合解決樹上的一些修改詢問以及求LCA之類的問題,同過把樹進行剖分,我們可以用很多數據結構維護每一條鏈,然后再通過求LCA,我們可以解決對于樹上任意兩點的詢問問題。例如不斷的對邊權修改,求任意兩點之間的最大邊或最大值,這就可以用線段樹對每個樹鏈進行維護。另外,還值得一提的是樹鏈剖分后求LCA的方法,由于我們在dfs2的時候記錄了top[]數組,所以用樸素法就能很快的求出LCA,代碼:
inline int LCA(int u, int v){ int tp1 = top[u], tp2 = top[v]; //tp為鏈頭 while (tp1 != tp2) { if (dep[tp1] < dep[tp2]) //判斷深度 { swap(tp1, tp2); swap(u, v); } u = fa[tp1]; //每次向上走了tp1所以速度很快 tp1 = top[u]; } return tp1;}令我不滿意的是,我好像還是沒有說清楚具體如何進行修改和查詢,好吧,下次結合具體的題目再說把,再給自己挖一個坑……
最后說說我最不擅長的復雜度吧,由于是按照輕重鏈剖分的,所以有這樣的定理:
1、對于輕邊(u,v),size[v]<=size[u]/2
2、從根到某一點的路徑上,不超過logN條輕邊
3、重鏈總數不超過logN
因此剖分的時間復雜度為O(N);用數據結構去維護每一條鏈時,每次的時間復雜度為O(logN),當然你若不是用高級數據結構我也沒辦法咯=_=……,所以總的來說時間復雜度是O(NlogN)級別的。
最后,我又看了一下百度,我覺的百科說的還是更有道理,樹鏈剖分是用來維護樹的路徑信息的一種算法?數據結構?我還是不來定義了……
對了,那個坑我會填的^_^~~~
新聞熱點
疑難解答