樹鏈剖分思想不是很複雜。首先給出幾個定義吧:
其核心思想就是,將一棵樹拆成多條鏈,然後對於每一條鏈,就用資料結構去維護。
有個不會證明的性質,就是如果將一顆樹拆成多條重鏈和輕邊,那麼重鏈的個數不會超過\(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到根...