樹鏈剖分可以把樹分成若干條鏈,從而維護樹上的路徑資訊。本質思想是把樹剖成可以用線性結構儲存的結構,然後可以資料結構維護。
分為三種:重鏈剖分、長鏈剖分、實鏈剖分。以下以重鏈剖分為主。
重鏈剖分可以將樹上的任意一條路徑劃分成不超過o(l
ogn)
o(logn)
o(logn
)條連續的鏈,每條鏈上的點深度互不相同(即是自底向上的一條鏈,鏈上所有點的 lca 為鏈的乙個端點)。
重鏈剖分還能保證劃分出的每條鏈上的節點 dfs 序連續,因此可以方便地用一些維護序列的資料結構(如線段樹)來維護樹上路徑的資訊。
相比之下,樹鏈剖分能解決的很多問題用樹上差分也能解決,兩者的關係近似於於線段樹和差分的關係。
重子節點:其子節點中子樹最大的子結點。如果有多個子樹最大的子結點,取其一。如果沒有子節點,就無重子節點。
輕子節點:除了重子節點的所有子節點。
重邊:從任意節點到重子節點的邊。
輕邊:從任意節點到輕子節點的邊。
重鏈:若干條首尾銜接的重邊,也包括了落單的節點。
如下圖 給出了具體的解釋(圖源oi-wiki)
為了剖出上述重鏈,我們需要維護如下資訊:
由兩次dfs即可處理出這些資訊,第一次處理前四項,後一次處理後四項。
處理**:
void
dfs1
(int u,
int f)}}
void
dfs2
(int u,
int f)
}
主要思想是重鏈內部的dfn序連續,所以我們只要以重鏈為基礎操作單位然後重鏈內部也可以單獨計算,和分塊之後的處理方法有點點相似的地方。
void
opchain
(int x,
int y)
//外界處理函式介面
solve
(dfn[top[x]
], dfn[x]);
x = fa[top[x]];
}if(dep[x]
> dep[y]
)swap
(x, y)
;solve
(dfn[x]
, dfn[y]);
}
#include
#include
#include
using
namespace std;
typedef
long
long ll;
const
int maxn =
1e5+5;
#define lson root << 1
#define rson root << 1 | 1
#define tr t[root]
int n, m, r, p;
vector<
int> g[maxn]
;int w[maxn]
, fa[maxn]
, son[maxn]
, dep[maxn]
, siz[maxn]
;int top[maxn]
, dfn[maxn]
, rnk[maxn]
;int tim;
struct segment_tree t[maxn <<2]
;void
pushdown
(int root)
}void
build
(int l,
int r,
int root)
int mid =
(l + r)
>>1;
build
(l, mid, lson)
;build
(mid +
1, r, rson)
; tr.val =
(t[lson]
.val + t[rson]
.val)
% p;
}void
modify
(int l,
int r,
int root,
int x)
int mid =
(tr.l + tr.r)
>>1;
pushdown
(root);if
(l <= mid)
modify
(l, r, lson, x);if
(r > mid)
modify
(l, r, rson, x)
; tr.val =
(t[lson]
.val + t[rson]
.val)
% p;
}int
query
(int l,
int r,
int root)
int mid =
(tr.l + tr.r)
>>1;
int ret =0;
pushdown
(root);if
(l <= mid) ret +
=query
(l, r, lson);if
(r > mid) ret +
=query
(l, r, rson)
;return ret % p;
}void
dfs1
(int u,
int f)}}
void
dfs2
(int u,
int f)
}void
mchain
(int x,
int y,
int z)
modify
(dfn[top[x]
], dfn[x],1
, z)
; x = fa[top[x]];
}if(dep[x]
> dep[y]
)swap
(x, y)
;modify
(dfn[x]
, dfn[y],1
, z);}
intqchain
(int x,
int y)
ret =
(ret +
query
(dfn[top[x]
], dfn[x],1
))% p;
x = fa[top[x]];
}if(dep[x]
> dep[y]
)swap
(x, y)
; ret =
(ret +
query
(dfn[x]
, dfn[y],1
))% p;
return ret;
}int
main()
dfs1
(r,0);
dfs2
(r, r)
;build(1
, n,1)
;while
(m--)}
return0;
}
樹鏈剖分 學習整理
在一棵樹上進行路徑的修改 求極值 求和 乍一看只要線段樹就能輕鬆解決,實際上,僅憑線段樹是不能搞定它的。我們需要用到一種貌似高階的複雜演算法 樹鏈剖分。樹鏈,就是樹上的路徑。剖分,就是把路徑分類為重鏈和輕鏈。重兒子 siz u 為v的子節點中siz值最大的,那麼u就是v的重兒子。輕兒子 v的其它子節...
樹鏈剖分 學習整理
重兒子 siz u 為v的子節點中siz值最大的,那麼u就是v的重兒子。輕兒子 v的其它子節點。重邊 點v與其重兒子的連邊。輕邊 點v與其輕兒子的連邊。重鏈 由重邊連成的路徑。輕鏈 輕邊。剖分後的樹有如下性質 性質1 如果 v,u 為輕邊,則siz u 2 siz v 性質2 從根到某一點的路徑上輕...
Query on a tree 樹鏈剖分整理
樹鏈剖分整理 樹鏈剖分就是把樹拆成一系列鏈,然後用資料結構對鏈進行維護。通常的剖分方法是輕重鏈剖分,所謂輕重鏈就是對於節點 u的所有子結點v,size v 最大的v與u 的邊是重邊,其它邊是輕邊,其中 size v 是以v 為根的子樹的節點個數,全部由重邊組成的路徑是重路徑,根據 上的證明,任意一點...