Just Wait...

重链剖分

树链剖分

一、前置知识

  • DFS序
  • LCA最近公共祖先
  • 线段树

二、定义

1.重儿子 \(son_u\)

节点 \(u\) 的儿子中, 子树最大的一个儿子称为重儿子。

2.重边

父亲节点连向它的重儿子的边,称作重边。

3.重链

几条重边所组成的链叫重链,特别地,单个的点也可以称作一条重链。

4.\(top_u\) 重链顶

一条重链中深度最小的节点

三、树剖基础

顾名思义,他的做法就是分离出来每一条重链
由于求重链和重链顶不可以同时进行,因此这里有两个 DFS,来预处理

DFS1:

void dfs1(int u){
	sz[u] = 1;
	for(int i = hd[u]; i; i = ed[i].nxt){
		int v = ed[i].to;
		if(v == fa[u]) continue;
		fa[v] = u;//这里需要直接预处理fa[v],后面要用
		dep[v] = dep[u] + 1;//预处理深度
		dfs1(v);
		sz[u] += sz[v];//预处理子树大小(求重儿子)
		if(sz[v] > sz[son[u]]) son[u] = v;//重儿子
	}
}

DFS2

void dfs2(int u, int t){//t为当前重链顶
	top[u] = t, ver[++ ind] = a[u], dfn[u] = ind;//处理重链顶和DFS序(求LCA和线段树)
	if(son[u]) dfs2(son[u], t);//只有重儿子才配当前的重链顶
	for(int i = hd[u]; i; i = ed[i].nxt){
		int v = ed[i].to;
		if(v == fa[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}

四、树剖LCA

思考:既然我们把一棵树都分成了一堆链,那么 LCA 就很好办了
两个节点一直往上跳重链顶,若两个点处于同一个链上,那么深度较深的哪个点就是 LCA

int lca(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}

五、树剖线段树

这种算法可以维护一个上的信息(相较于线段树维护序列信息)。

具体例题可以看 (模板)树链剖分/重链剖分

如何把一棵树映射成一个序列呢?
可以记录下来这棵树的 DFS 序, 然后在这个序列上面进行操作,对于子树的问题,节点 \(u\) 的子树在 DFS 序中对应区间 \([dfn[u], dfn[u] + sz[u] - 1]\) 那么后两个问题迎刃而解。

那么,更进一步地,可以在 DFS 序中优先考虑重儿子。那么对于一条链上的操作就可以像上面的 LCA 一样,分成若干重链,在每个重链上面分别进行操作就可以了。

void add(int u, int v, int x){//链上加
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		mdf(dfn[top[u]], dfn[u], x);//线段树修改
		u = fa[top[u]];
	}
	if(dep[u] > dep[v]) swap(u, v);
	mdf(dfn[u], dfn[v], x);
} 
int sum(int u, int v){//链上求和
	int res = 0;
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		res += qry(dfn[top[u]], dfn[u]);//线段树求和
		u = fa[top[u]];
	}
	if(dep[u] > dep[v]) swap(u, v);
	return res + qry(dfn[u], dfn[v]);
} 

总结

反正都是板子,改的地方很少...

Code

int fa[N], son[N], sz[N], top[N], dep[N], ver[N], dfn[N], ind;

namespace segtree{
	#define ls (u << 1)
	#define rs (u << 1 | 1)
	#define mid (l + r >> 1)
	#define segroot int u = 1, int l = 1, int r = n
	#define lson ls, l, mid
	#define rson rs, mid + 1, r
	struct { int sum, add, len; } t[N << 2];
	
	void up(int u) { t[u].sum = t[ls].sum + t[rs].sum; }
	
	void build(segroot){
		t[u].len = r - l + 1;
		if(l == r){ t[u].sum = ver[l]; return; }
		build(lson); build(rson); up(u);
	}
	
	void down(int u, int x){ t[u].add += x, t[u].sum += t[u].len * x; }
	
	void down(int u) { down(ls, t[u].add), down(rs, t[u].add), t[u].add = 0; };
	
	int qry(int ql, int qr, segroot) {
		if( qr < l || r < ql ) return 0;
		if( ql <= l && r <= qr ) return t[u].sum;
		down(u);
		return qry(ql, qr, lson) + qry(ql, qr, rson);
	}
	
	void mdf(int ql, int qr, int x, segroot) {
		if( qr < l || r < ql ) return;
		if( ql <= l && r <= qr ) return down(u, x);
		down(u), mdf(ql, qr, x, lson), mdf(ql, qr, x, rson), up(u);
	}
}

using namespace segtree;

struct Node{
	int to, nxt;
} ed[N << 1];

int hd[N << 1], cnt;

void addedge(int u, int v){
	ed[++ cnt] = {v, hd[u]};
	hd[u] = cnt;
}

void dfs1(int u){
	sz[u] = 1;
	for(int i = hd[u]; i; i = ed[i].nxt){
		int v = ed[i].to;
		if(v == fa[u]) continue;
		fa[v] = u;
		dep[v] = dep[u] + 1;
		dfs1(v);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]]) son[u] = v;
	}
}

void dfs2(int u, int t){
	top[u] = t, ver[++ ind] = a[u], dfn[u] = ind;
	if(son[u]) dfs2(son[u], t);
	for(int i = hd[u]; i; i = ed[i].nxt){
		int v = ed[i].to;
		if(v == fa[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}

void add(int u, int v, int x){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		mdf(dfn[top[u]], dfn[u], x);
		u = fa[top[u]];
	}
	if(dep[u] > dep[v]) swap(u, v);
	mdf(dfn[u], dfn[v], x);
} 

int sum(int u, int v){
	int res = 0;
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		res += qry(dfn[top[u]], dfn[u]);
		u = fa[top[u]];
	}
	if(dep[u] > dep[v]) swap(u, v);
	return res + qry(dfn[u], dfn[v]);
} 
posted @ 2025-08-30 17:38  Hty111  阅读(9)  评论(0)    收藏  举报