算法詳解——樹分治

樹分治

一句話講,把分治做到樹上。

樹分治首先要把無根樹轉成有根樹(如果是無根樹,當然有根樹就直接分治),即找一個點Root 作爲根。

如何找根?

爲了分治的時效,我們需要分治的層數越少越好,於是想讓找到的根下最大子樹的節點越少越好,我們便可以用一趟dfs 來刷。
這裏需要了解幾個數組:Fi 表示i 節點下最大子樹的節點數,Sizei 表示i 節點所在子樹的節點數。
我們在遍歷到j 這個點時,Sizefaj+=SizejFfaj=max{Sizej} 。因爲用dfs ,所以逆推就好了。

void getrt(int x,int fa){ //找根
    F[x]=0;Size[x]=1; //初始化
    for (int i=lnk[x];i;i=nxt[i]){ //遍歷下一個點
        if (vis[son[i]]||son[i]==fa) continue; //防止循環
        getrt(son[i],x); //dfs下去
        Size[x]+=Size[son[i]];
        F[x]=max(F[x],Size[son[i]]); //修正
    }
    F[x]=max(F[x],Sum-Size[x]); //Sum是整棵樹的節點數,也就是Sum=N
    if (F[x]<F[Root]) Root=x; //修改Root
}

注意,代碼中有一句F[x]=max(F[x],Sum-Size[x]);,是因爲x 節點沒法遍歷到fa 節點,但是以x 爲根,fa 節點所在的子樹沒有被統計,所以最後要補上一道。
PS:所求的點又叫重心

正題

現在進入正題——樹分治
我們可以把樹上的每個節點看成一個人,整棵樹看成一個公司,老闆(根節點)有一項任務,於是找來了所有部門,把任務下發;而每個部門又可以看成一棵樹,部長(子樹的根節點)找來了該部門的所有員工,把任務下發,以此類推。這樣就是分治。
那麼接下來就很簡單了。從根節點開始,下面每個節點找一下重心,繼續分治下去即可。

inline void Solve(int x){ //分治
    vis[x]=1;
    for (int i=lnk[x];i;i=nxt[i]){
        if (vis[son[i]]) continue; //已經分過了就不要再分,防止循環
        Root=0;Sum=Size[son[i]]; 
        //初始化,這裏Sum=Size[son[i]],是因爲這棵子樹從整棵樹上摘下來,也就是公司不管部門的事
        getrt(son[i],0); //找子樹的重心
        Solve(Root); //繼續分下去
    }
}

好了,沒了,就這麼簡單。

例題

poj1741 Tree
bzoj2152聰聰可可 雙倍經驗
洛谷

還有一種邊分治,但我這種蒟蒻555

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章