樹鏈剖分學習筆記

2022-08-03 03:21:14 字數 4658 閱讀 4704

我們所常說的樹鏈剖分其實是輕重鏈剖分

樹鏈剖分可以處理樹上的任意兩點間路徑和任意一點子樹的資訊修改與查詢(配合線段樹這樣的資料結構..)

建議先學會線段樹和 lca。

首先注意下文中權值是賦在點上的 而不是在邊上

如果遇到權值在邊上的情況 把權值賦給這條邊連線的兩點中深度較大的那個點即可

線段樹是通過維護一些區間 並且把待處理區間拆分成一定數量個維護的區間

樹鏈剖分思想相似 將樹剖分成\(\log n\)級別條互相不相交的鏈同時保證每乙個點都在且僅在一條鏈上(所有鏈可以覆蓋所有點) 對於每一條路徑可以將其拆分成\(\log n\)級別條鏈分別維護鏈上點權

鏈就是一些點 這些點首尾相接 除了兩端的點只有乙個點與之相連 其他的點都有兩點與之有邊相連

在這裡的鏈中 鏈維護一些深度遞增的點 沒有任意兩點在樹中深度相同

子樹的問題在後文提到

如果將一條很長的鏈一起處理 可以優化效率

為了使得鏈與鏈不相交 必定有一些邊不能在鏈中 我們把這些邊成為輕邊因為這樣的邊不會很多 遇到時直接處理就行了

於是我們通過一定形式將一棵樹分成輕邊和鏈 所有的鏈都一起處理 而對於輕邊則直接一條一條邊處理

就是為了把\(n\)變成\(\log n\)啊

我們把鏈稱為重鏈

首先我們要讓重鏈的取法最優

我們對於每乙個點 顯然它到它兒子(我們先隨便指定乙個點當做根)的所有出邊中只有一條邊能在重鏈中否則鏈會相交

在這裡我們取子樹大小最大的兒子作為重兒子 對於任意乙個點該點和它的重兒子位於同一條重鏈上 到其餘的兒子的路徑作為輕邊

這樣就會形成一些重鏈了 如果覺得太抽象可以看看下面的

至於為什麼取子樹大小最大在後面複雜度證明中會提到

首先我們要通過dfs預處理出每乙個點的子樹大小、父親節點編號、深度、重兒子編號。

這些都不難處理 在下面的**中給出注釋了

這時候這棵樹已經剖分好了 但是為了維護鏈上資訊 我們還要再處理一些資訊

首先每一條鏈可以視為乙個區間 我們可以用線段樹來維護鏈資訊

等等 還沒結束

現在我們是不知道每一條重鏈對應的區間編號

所以對於每乙個點我們還要處理出它所在重鏈的頂端的點(該鏈上深度最小的點)編號

如果乙個點在重鏈的中間 那麼只用處理這條重鏈的一部分就行了(該點到頂端的部分) 鏈上的點對應線段樹中編號是隨深度遞增的

如果路徑上的兩個點都在同一條重鏈上 只要處理這兩個點之間的部分就行了

最後放張圖方便理解 **在最後

code:

void dfs1(int u,int f)	}}

inline void dfs2(int u,int f)

}dfs1(s,0); dfs2(s,s);

// s為根 注意根節點所在重鏈頂端是它自己

x到y的樹上路徑一定為\(x->lca(x,y)->y\)

這個過程相當於把乙個點先移動到lca 在把另乙個點移動到lca所在重鏈上 再處理這兩點之間的部分

我們的目標狀態是兩個點移動到同一條重鏈上

直到兩點在同一條重鏈上

然後修改/查詢兩點之間的部分即可

查詢還需要把那幾條鏈的資訊首尾拼接起來,求和或求最值的時候,合併就是所有鏈查詢得到的數求和/取最值了,但有些時候可能會比較複雜,要用合併線段樹左右兒子資訊的方式合併鏈。

如果兩點不在同一條重鏈上 這兩點的lca的所有兒子中一定有乙個重兒子和大於等於乙個非重兒子的子節點這兩個點在移動過程中必定會有乙個點移動到重兒子所在鏈的鏈上這樣以後就會移動另乙個點(之前那個點所在鏈頂端淺於或等於lca)到某個輕兒子的父親上 也就是移動到了lca

code:

void update_chain(ll x,ll y,ll v)

if(dep[x] > dep[y]) swap(x,y);

update(1,1,n,id[x],id[y],v);

}

code:

void update_son(ll x,ll v)
看完2應該就知道了

一樣的處理方式 最後兩個點移動到同一條重鏈上時較淺的點為lca

code:

int lca(int x,int y)

return (dep[x] > dep[y] ? y : x);

}

子樹大小最大的兒子作為重兒子

所以從任意一點開始走 每走一條輕邊 走完後的點子樹大小相對於原來的點至少減少到原來一半

所以之多經過\(logn\)條輕邊

然後由於重鏈是一起處理的 每經過一條重鏈後會再經過一條輕鏈 然後再經過下一條重鏈

相當於每一條重鏈都是在兩條輕邊之間的 所以重鏈條數也不會超過\(log_2n\)條

而且 顯然這個上界比較松 跑不滿

加上線段樹的\(logn\) 樹鏈剖分一次的複雜度為\(log^2n\)

樹剖常數很小 n<=100000的時候可以吊打少乙個log的lct

p3384模板題

古時候的**了 現在碼風都變了

#include #include #include #include #include #include #include #define ll long long

#define mid ((l + r) >> 1)

#define lson (x << 1)

#define rson ((x << 1) | 1)

using namespace std;

ll n,q,r,n,a,b;

ll w[200005] = ;

ll to[400005] = ,hed[400005] = ,nxt[400005] = ,cnt = 0;

ll fa[200005] = ,dep[200005] = ,siz[200005] = ,son[200005] = ,top[200005] = ,id[200005] = ,rid[200005] = ;

inline void add_edge(ll f,ll t)

//segment tree +

ll val[800005] = ;

ll add[800005] = ;

void build(int x,int l,int r)

build(lson,l,mid);

build(rson,mid + 1,r);

val[x] = (val[lson] + val[rson]) % n;

}void push(int x,int l,int r)

void update(int x,int l,int r,int l,int r,ll k)

push(x,l,r);

if(l <= mid) update(lson,l,mid,l,r,k);

if(r > mid) update(rson,mid + 1,r,l,r,k);

val[x] = (val[lson] + val[rson]) % n;

}ll query(int x,int l,int r,int l,int r)

push(x,l,r);

if(l <= mid) sum = (sum + query(lson,l,mid,l,r)) % n;

if(r > mid) sum = (sum + query(rson,mid + 1,r,l,r)) % n;

return sum;

}//segment tree

//inline void dfs1(ll u,ll f) }}

inline void dfs2(ll u,ll f)}//

//inline void update_son(ll x,ll v)

inline ll query_son(ll x)

inline void update_chain(ll x,ll y,ll v)

if(dep[x] > dep[y]) swap(x,y);

update(1,1,n,id[x],id[y],v);

}inline ll query_chain(ll x,ll y)

if(dep[x] > dep[y]) swap(x,y);

ans += query(1,1,n,id[x],id[y]);

ans %= n;

return ans;}//

int main()

cnt = 0;

dfs1(r,0);

dfs2(r,r);

build(1,1,n);

while(q --)

if(op == 2)

if(op == 3)

if(op == 4)

} return 0;

}

配合線段樹 樹鏈剖分還可以實現很多操作

樹鏈剖分學習筆記

寫 又犯了很sb的錯誤,線段樹寫錯了。好像每次都會把r l 1寫成l r 1,然後就只有20分。寫的比較醜,壓了壓之後190行。基本上是我打過的最長的乙個模板了 然後簡單介紹一下樹剖吧。樹鏈剖分,就是把樹剖分成鏈,然後用資料結構來維護這些鏈,使得詢問 修改的複雜度達到o logn o l ogn 不...

樹鏈剖分學習筆記

樹鏈剖分 mod estc oder modestcoder modest code r如果你是重兒子,你就在重路徑上。如果你是輕兒子,暴力沿著祖先向上爬最多log nlogn logn 次就可以遇到重路徑。或者到根 而樹上操作基本就是找祖先 也許有人喜歡我的碼風 include include d...

樹鏈剖分學習筆記

前言 書上只講了重鏈剖分,菜雞也只會這一種,想看其他的是別想了。要會樹鏈剖分,首先你需要了解一些概念。我們把乙個節點的所有兒子節點中子樹節點數最大的稱為重兒子,也就是size最大的子節點。size的定義我在講換根dp時說過,因此不再贅述。對於每個節點的重兒子,我們用 son x 來記錄它,父親節點到...