树链剖分

树链剖分是个好东西呀
我挺喜欢用树剖求(LCA)
因为我不太会打倍增

接下来就稍微说一下树剖的实现
首先我们先引入几个概念:

  • 重儿子:这个点的子树中(siz)最大的
  • 轻儿子:子节点中除了重儿子的节点都是轻儿子
  • 重边:与重儿子相连的边
  • 轻边:与轻儿子相连的边
  • 重链:重边组成的链叫重链
    然后我们我还要知道跳重链是个什么东东

就以这张我在网上扒的图片为例吧
这张图里的所有黑色加粗的边都是重边
我们用(top)记录一下当前这条重链的顶端就好了
例如:(14)那条重链的顶端就是(1),而(11)那条重链的顶端就是(2)
然后我们就可以根据这个来进行操作了

(LCA)

(LCA)的主要思路就是跳重链,直到两个点跳到同一条重链上为止,并且深度较小的那个就是要求的(LCA),我们可以模拟求一下(12)(7)(LCA)
首先我们可以知道(7)(12)并不在同一条重链上,所以我们要通过跳重链来使这两个点跳到一条重链上去
我们要让所处链的(top)深的先跳,直到跳到与那个浅的(top)同高或比它浅为止
我们就可以看出我们就要让(12)先跳到(2->6->11)这条重链上去(即跳到(fa[top])上去),然后我们就可以在让(12)向上跳,我们就会发现,
它会跳到了(1)的位置,然后我们就可以让(7)跳了,(7)跳一次重链就到达了(1),然后就可以得出结论(7)(12)(LCA)就是(1)
我们简化为:

[12->6->1 ]

[7->1 ]

然后就看一道模板题
题目
其实就是求(LCA)的裸题(下面会有两种代码,但是我个人比较爱写树剖的这个)

倍增版:
#include<bits/stdc++.h>
using namespace std;
const int N = 500000 + 5;
int n, m, root, head[N], cnt, hig[N], f[N][21];
struct node{
	int u, v, nxt;
}edge[N << 1];
void add(int u, int v){
	edge[++ cnt].u = u;
	edge[cnt].v = v;
	edge[cnt].nxt = head[u];
	head[u] = cnt;
}
void add_tree(int x){
	for(int i = 1; i <= 19; i ++){
		f[x][i] = f[f[x][i - 1]][i - 1];
	}
	for(int i = head[x]; i; i = edge[i].nxt){
		if(edge[i].v == f[x][0]) continue;
		f[edge[i].v][0] = x;
		hig[edge[i].v] = hig[x] + 1;
		add_tree(edge[i].v);
	}
}
int ask(int x, int y){
	if(hig[x] < hig[y]) swap(x, y);
	for(int i = 19; i >= 0; i --){
		if(hig[f[x][i]] >= hig[y]) x = f[x][i];
	}
	if(x == y) return x;
	for(int i = 19; i >= 0; i--){
		if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	}
	return f[x][0];
}
int read(){
	int op = 0, opp = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){ if(ch == '-') opp = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){ op = (op << 1) + (op << 3) + (ch - '0'); ch = getchar();}
	return op * opp;
}
int main(){
	n = read(), m = read(), root = read();
	for(int i = 1, x, y; i < n; i ++){
		x = read(), y = read();
		add(x, y); add(y, x);
	}
	hig[root] = 1;
	add_tree(root);
	for(int i = 1, x, y; i <= m; i ++){
		x = read(), y = read();
		printf("%d
", ask(x, y));
	}
	return 0;
}
树剖版
#include<bits/stdc++.h>
using namespace std;
const int N = 500000 + 5;
int n, m, root, head[N], fa[N], top[N], cnt, hig[N], siz[N], hev[N];
struct node{
	int u, v, nxt;
}edge[N << 1];
void add(int u, int v){
	edge[++ cnt].u = u;
	edge[cnt].v = v;
	edge[cnt].nxt = head[u];
	head[u] = cnt;
}
void dfs1(int x){
	siz[x] = 1;
	for(int i = head[x]; i; i = edge[i].nxt){
		int to = edge[i].v;
		if(to == fa[x]) continue;
		fa[to] = x;
		hig[to] = hig[x] + 1;
		dfs1(to);
		siz[x] += siz[to];
		if(siz[to] > siz[hev[x]])	hev[x] = to;
	}
}
void dfs2(int x, int topx){
	top[x] = topx;
	if(hev[x]) dfs2(hev[x], topx);
	for(int i = head[x]; i; i = edge[i].nxt){
		int to = edge[i].v;
		if(to == fa[x] || to == hev[x]) continue;
		dfs2(to, to);
	}
}
int ask(int x, int y){
	while(top[x] != top[y]){
		if(hig[top[x]] < hig[top[y]]) swap(x, y);
		x = fa[top[x]];
	}
	return hig[x] < hig[y] ? x : y;
}
int read(){
	int op = 0, opp = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){ if(ch == '-') opp = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){ op = (op << 1) + (op << 3) + (ch - '0'); ch = getchar();}
	return op * opp;
}
int main(){
	n = read(), m = read(), root = read();
	for(int i = 1, x, y; i < n; i ++){
		x = read(), y = read();
		add(x, y); add(y, x);
	}
	hig[root] = 1;
	dfs1(root);
	dfs2(root, root);
	for(int i = 1, x, y; i <= m; i ++){
		x = read(), y = read();
		printf("%d
", ask(x, y));
	}
	return 0;
}

(LCA)其实有很多种方法的,只不过因为(Tethys)太菜了,只会这两种,所以就先占个坑吧

原文地址:https://www.cnblogs.com/wsdslll/p/13397750.html