1. 程式人生 > 其它 >[模板] 樹鏈剖分(輕重鏈剖分)

[模板] 樹鏈剖分(輕重鏈剖分)

一些定義

  • 重兒子:

結點所有兒子裡子樹規模最大的結點,即\(sz[ x ]\) 最大。

我第一次接觸這個概念居然不是在這,而是在這。

P5666 CSP-S2019 樹的重心

有時間再寫題解吧,這是一個不錯的思維題。

  • 重鏈:

從重兒子一直延伸到葉子結點的路徑。

  • 樹鏈:

任意兩點間的距離 ( 不嚴謹的說。

原理:

通過有序的 dfs

先跑重兒子,回來再跑輕兒子,最後形成一個特殊的 dfs 序。

兩遍 dfs

\(1.\) 處理出需要的資訊:

\(de , fa , sz , top\)(所處於重鏈的起點編號)。

\(2.\) 跑特殊的 \(dfs\) ,形成 \(dfs\) 序。

\(dfs\)
序的性質

  • 一條鏈上的序是連續的。

  • 一棵子樹內的 dfs 序為 \(dfn[x]\)\(dfn[x] + sz[x] + 1\),也是連續的。

樹鏈剖分要解決的問題

給一棵子樹或者一條樹鏈上的結點全部加上一個權值。

樹鏈剖分的主演算法

利用特殊 dfs 序的性質,分幾步來看:

假設現在要將樹鏈 \((x , y)\)上的權值加上某個數。

\(1.\) 如果 \(x\)\(y\) 所處鏈不同,選取深度較大的結點爬到鏈端處,一邊跑一邊把路徑上的結點加上權值 (感性理解)。

\(2.\) 最後肯定有一個結點跑到 \(x\)\(y\)\(lca\) 處。

\(3.\)

處理 \(LCA\) 到另外一個結點的那段就可。

inline int qRange(int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        res=0;
        query(1,1,n,id[top[x]],id[x]);
        ans+=res;
        ans%=mod;
        x=fa[top[x]];
    }
    
    if(dep[x]>dep[y])swap(x,y);
    res=0;
    query(1,1,n,id[x],id[y]);
    ans+=res;
    return ans%mod;
}

最後用線段樹維護區間和即可。