程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 樹鏈剖分原理

樹鏈剖分原理

編輯:C++入門知識

樹鏈剖分用一句話概括就是:把一棵樹剖分為若干條鏈,然後利用數據結構(樹狀數組,SBT,Splay,線段樹等等)去維護每一 條鏈,復雜度為O(logn)     那麼,樹鏈剖分的第一步當然是對樹進行輕重邊的劃分。 定義size(x)為以x為根的子樹節點個數,令v為u的兒子中size值最大的節點,那麼(u,v)就是重邊,其余邊為輕邊。   當然,關於這個它有兩個重要的性質: (1)輕邊(u,v)中,size(v)<=size(u/2) (2)從根到某一點的路徑上,不超過logn條輕邊和不超過logn條重路徑。     當然,剖分過程分為兩次dfs,或者bfs也可以。   如果是兩次dfs,那麼第一次dfs就是找重邊,也就是記錄下所有的重邊。 然後第二次dfs就是連接重邊形成重鏈,具體過程就是:以根節點為起點,沿著重邊向下拓展,拉成重鏈,不在當前重鏈上的節 點,都以該節點為起點向下重新拉一條重鏈。     剖分完畢後,每條重鏈相當於一段區間,然後用數據結構去維護,把所有重鏈首尾相接,放到數據結構上,然後維護整體。   在這裡,當然有很多數組,現在我來分別介紹它們的作用:   siz[]數組,用來保存以x為根的子樹節點個數 top[]數組,用來保存當前節點的所在鏈的頂端節點 son[]數組,用來保存重兒子 dep[]數組,用來保存當前節點的深度 fa[]數組,用來保存當前節點的父親 tid[]數組,用來保存樹中每個節點剖分後的新編號 rank[]數組,用來保存當前節點在線段樹中的位置   那麼,我們現在可以根據描述給出剖分的代碼: 第一次dfs:記錄所有的重邊  

void dfs1(int u,int father,int d)  
{  
    dep[u]=d;  
    fa[u]=father;  
    siz[u]=1;  
    for(int i=head[u];~i;i=next[i])  
    {  
        int v=to[i];  
        if(v!=father)  
        {  
            dfs1(v,u,d+1);  
            siz[u]+=siz[v];  
            if(son[u]==-1||siz[v]>siz[son[u]])  
                son[u]=v;  
        }  
    }  
}  

 

    第二次dfs:連重邊成重鏈  
void dfs2(int u,int tp)  
{  
    top[u]=tp;  
    tid[u]=++tim;  
    rank[tid[u]]=u;  
    if(son[u]==-1) return;  
    dfs2(son[u],tp);  
    for(int i=head[u];~i;i=next[i])  
    {  
        int v=to[i];  
        if(v!=son[u]&&v!=fa[u])  
            dfs2(v,v);  
    }  
}  

 

    當然,由於題目有時候要求邊很多,所以最好不要用二維數組表示邊,應用鄰接表或者鏈式前向星。     當然,這裡面有一個重要的操作,那就是修改樹中邊權的值。   如何修改u到v的邊權的值呢?這裡有兩種情況: (1)如果u與v在同一條重鏈上,那麼就直接修改了 (2)如果u與v不在同一條重鏈上,那麼就一邊進行修改,一邊將u與v往同一條重鏈上靠,這樣就變成了第一種情況了   那麼現在的關鍵問題就是如何將u和v往同一條重鏈上靠?這個問題此處我就省略了。   至此,樹鏈剖分原理基本分析完畢!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved