數鏈剖分基礎講解

2021-07-05 13:40:37 字數 2689 閱讀 6279

「在一棵樹上進行路徑的修改、求極值、求和」乍一看只要線段樹就能輕鬆解決,實際上,僅憑線段樹是不能搞定它的。我們需要用到一種貌似高階的複雜演算法——樹鏈剖分。

樹鏈,就是樹上的路徑。剖分,就是把路徑分類為重鏈和輕鏈。

重兒子:siz[u]為v的子節點中siz值最大的,那麼u就是v的重兒子。

輕兒子:v的其它子節點。

重邊:點v與其重兒子的連邊。

輕邊:點v與其輕兒子的連邊。

重鏈:由重邊連成的路徑。

輕鏈:輕邊。

剖分後的樹有如下性質:

性質1:如果(v,u)為輕邊,則siz[u] * 2 < siz[v];

性質2:從根到某一點的路徑上輕鏈、重鏈的個數都不大於logn。

演算法實現:

我們可以用兩個dfs來求出fa、dep、siz、son、top、w。

dfs_1:把fa、dep、siz、son求出來,比較簡單,略過。

⒉對於v的各個輕兒子u,顯然有top[u] = u,並且w[u] = totw+1,進行dfs_2過程。

這就求出了top和w。

修改操作:例如將u到v的路徑上每條邊的權值都加上某值x。

一般人需要先求lca,然後慢慢修改u、v到公共祖先的邊。而高手就不需要了。

記f1 = top[u],f2 = top[v]。

當f1 <> f2時:不妨設dep[f1] >= dep[f2],那麼就更新u到f1的父邊的權值(logn),並使u = fa[f1]。

當f1 = f2時:u與v在同一條重鏈上,若u與v不是同一點,就更新u到v路徑上的邊的權值(logn),否則修改完成;

重複上述過程,直到修改完成。

求和、求極值操作:類似修改操作,但是不更新邊權,而是對其求和、求極值。

就這樣,原問題就解決了。鑑於鄙人語言表達能力有限,咱畫圖來看看:

當要修改11到10的路徑時。

第一次迭代:u = 11,v = 10,f1 = 2,f2 = 10。此時dep[f1] < dep[f2],因此修改線段樹中的5號點,v = 4, f2 = 1;

第二次迭代:dep[f1] > dep[f2],修改線段樹中10--11號點。u = 2,f1 = 2;

第三次迭代:dep[f1] > dep[f2],修改線段樹中9號點。u = 1,f1 = 1;

第四次迭代:f1 = f2且u = v,修改結束。

**資料規模大時,遞迴可能會爆棧,而非遞迴dfs會很麻煩,所以可將兩個dfs改為寬搜+迴圈。即先寬搜求出fa、dep,然後逆序迴圈求出siz、son,再順序迴圈求出top和w。

題目:spoj375、usaco december contest gold divison, "grassplant"。

**spoj375據說不「縮行」情況下最短的程式是140+行,我的是128行。

附spoj375程式(c++):

#include

#include

#include

#include

using namespace std;

const int maxn = 10010;

struct tedge

e[maxn * 2];

int tree[maxn];

int zzz, n, z, edge, root, a, b, c;

int d[maxn][3];

int first[maxn], dep[maxn], w[maxn], fa[maxn], top[maxn], son[maxn], siz[maxn];

char ch[10];

void insert(int a, int b, int c)

void dfs(int v)

}void build_tree(int v, int tp)

void update(int root, int lo, int hi, int loc, int x)

int mid = (lo + hi) / 2, ls = root * 2, rs = ls + 1;

update(ls, lo, mid, loc, x);

update(rs, mid+1, hi, loc, x);

tree[root] = max(tree[ls], tree[rs]);

}int maxi(int root, int lo, int hi, int l, int r)

inline int find(int va, int vb)

tmp = max(tmp, maxi(1, 1, z, w[f1], w[va]));

va = fa[f1]; f1 = top[va];

} if (va == vb) return tmp;

if (dep[va] > dep[vb]) swap(va, vb);

return max(tmp, maxi(1, 1, z, w[son[va]], w[vb]));  //

}void init()

dfs(root);

build_tree(root, root); 

// for (int i = 1; i < n; i++) }

inline void read()

void work()

}int main()

return 0;

}

樹鏈剖分 樹鏈剖分講解

好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 vector v maxn int size maxn dep maxn val maxn id maxn hson maxn top maxn fa maxn 定義 int edge 1,num 1 struct tree e ma...

樹鏈剖分講解

題目 aragorn s story 題意 給一棵樹,每個結點都有初始的權值,有m個操作,分兩種 一是從x 結點到y 結點路上所有的結點權值 z或 z,二是問x結點的權值。思路 樹鏈剖分。這是我學樹剖的第一題,建議還沒接觸過的夥伴,第一次學習的時候不要一直糾結理論,直接找一道模板題,然後找一篇ac ...

數鏈剖分(Housewife Wind )

題目大意 給你n,q,s。n指的是有n個點,q代表有q次詢問,s代表的是起點。然後接下來會有n 1條邊,雙向邊,帶有權值,對於q次詢問,如果輸入的第乙個數是1,然後接下來會輸入兩個數,t1,t2。t帶邊將第t1條邊的權值改成t2.如果第乙個數是0,接下來會輸入乙個t,詢問從s到t的花費。具體思路 對...