樹鏈剖分總結

2022-03-02 03:39:01 字數 2469 閱讀 3236

樹鏈剖分思想不是很複雜。首先給出幾個定義吧:

核心思想就是,將一棵樹拆成多條鏈,然後對於每一條鏈,就用資料結構去維護。

有個不會證明的性質,就是如果將一顆樹拆成多條重鏈和輕邊,那麼重鏈的個數不會超過\(log_2n\),輕邊的邊數也不會超過\(log_2n\)。因為這個性質,很多操作我們可以很高效地完成。這個之後就知道了。

那麼我們如何去拆分一顆樹呢?通過兩次dfs即可解決。

首先第一次dfs,我們可以處理出每個結點的深度\(deep[u]\),以它為根的子樹中點的數量\(sz[u]\),以及每個點的父親結點\(fa[u]\),並且可以求出每個點的重兒子\(son[u]\)。

**如下:

void dfs1(int u, int pa, int d) 

}

之後進行第二次dfs,這裡我們就需要處理出鏈了。我們知道,dfs序可以將樹結構雜湊成線性結構,然後方便我們去維護。其實一般樹鏈剖分都要利用dfs序。但是這裡因為我們要維護一條鏈的資訊,所以我們應該在dfs的時候優先選擇重兒子,並且維護每條鏈的頂端結點,方便我們後續的操作。

優先選擇重兒子就可以保證一條鏈的dfs序是連續的,方便我們之後進行維護。

第二次dfs的**如下:

void dfs2(int u, int topf) 

}

這樣我們就處理出每條鏈了,top陣列儲存的就是每條鏈的頂端結點。對於兩點間的路徑,我們就可以利用top來加速。

以上就是樹鏈剖分的核心部分吧。其餘部分就是相應的資料結構去維護資訊了。

現在看起來不是很難,但是以前聽都聽不懂。。還是以前學習的態度不是很認真吧。

下面給出一道模板題吧:

[洛谷p3384](

題目中對於路徑的修改和詢問,我們就類似於求lca那樣往上面跳就行了,同時在跳的過程中,記得對鏈上面的資訊進行維護。

題目中對於子樹的修改和詢問,也類似,因為他們的dfs序都是連續的,所以直接通過dfs序來維護資訊就好啦。

以下為**(兩次dfs不長,加上線段樹就有點長了啊。。):

code

#include using namespace std;

typedef long long ll;

const int n = 2e5 + 5;

int n, m, r, mod;

int v[n];

int deep[n], son[n], fa[n], top[n], id[n], w[n], sz[n];

int cnt ;

int sum[4 * n], lazy[4 * n];

struct edgee[n << 1];

int head[n], tot;

void adde(int u, int v)

void dfs1(int u, int pa, int d)

}void dfs2(int u, int topf)

}void pushup(int o)

void pushdown(int o, int l, int r)

}void build(int o, int l, int r)

int mid = (l + r) >> 1;

build(o << 1, l, mid) ;

build(o << 1|1, mid + 1, r) ;

pushup(o) ;

}void update(int l, int r, int o, int l, int r,int val)

pushdown(o, l, r) ;

int mid = (l + r) >> 1;

if(l <= mid) update(l, r, o << 1, l, mid, val) ;

if(r > mid) update(l, r, o << 1|1, mid + 1, r, val) ;

pushup(o) ;

}int query(int l, int r, int o, int l, int r)

int main()

dfs1(r, 0, 1) ;

dfs2(r, r) ;

build(1, 1, n);

for(int i = 1; i <= m; i++)

if(deep[x] < deep[y]) swap(x, y) ;

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

} else if(op == 2)

if(deep[x] < deep[y]) swap(x, y) ;

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

cout << ans << '\n';

} else if(op == 3) else

}return 0;

}

未完待續...

樹鏈剖分總結

把樹剖成鏈再操作 部落格安利 維護7個不同陣列,通常和線段樹一起使用 1 模板題 p3384 模板 樹鏈剖分 p3178 haoi2015 樹上操作 p2590 zjoi2008 樹的統計 p2146 軟體包管理器 p2420 讓我們異或吧 2 應用題 p3950 部落衝突 p3038 牧草種植 1...

樹鏈剖分 樹鏈剖分講解

好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 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...

模板總結 樹鏈剖分

給定一顆樹,要求對樹上的路徑 u,v 進行高效操作。1 更新路徑 u,v 上所有點的權值,單點查詢樹上某點的權值 2 更新結點u的權值,查詢路徑 u,v 上的權值和 3 更新路徑 u,v 上的權值,查詢路徑 u,v 上的權值和 4 更新結點u的權值,查詢路徑 u,v 上的lcis 5 更新結點u到根...