树链剖分(重链剖分)总结

基本内容

基本思想

\qquad 树链剖分,顾名思义,是应用在树上的一种数据结构。一般用于处理动态维护路径信息、子树信息的问题,例如路径权值修改,路径查询权值和(最值),子树查询权值和(最值)等。树链剖分是将树剖析成一条条链,再利用 dfn \text{dfn} dfn 序上套线段树实现动态维护区间信息。

实现过程

step1: 重儿子、重链


\qquad 回顾标题:树链剖分(重链剖分),为什么要在小括号里加个重链剖分 呢?

树链剖分(树剖/链剖)有多种形式,如重链剖分长链剖分和用于 Link/cut Tree 的剖分(有时被称作「实链剖分」),大多数情况下(没有特别说明时),「树链剖分」都指「重链剖分」。—— from oi-wiki

\qquad 话说至此,什么又是重链剖分呢?在了解这之前,我们先来了解下什么是重儿子

\qquad “重”儿子,为什么说它“重”呢?在树上,什么信息可以恰当,形象的说“轻重”呢?当然是 size \text{size} size (子树大小)啦!我们形式化的定义一下重儿子:假设 son x \text{son}_x sonx 表示点 x \text{x} x 的重儿子, to x \text{to}_x tox 表示 x \text{x} x 的儿子们, sze x \text{sze}_x szex 表示以 x \text{x} x 为根的子树的大小,那么 sze son x = max ⁡ u ∈ to x sze u \large \text{sze}_{\text{son}_x}=\max_{u\in \text{to}_x}\text{sze}_u szesonx=maxutoxszeu。用语言描述就是:重儿子的子树大小是所有子节点中最大的。我们把剩余的子节点定义为轻儿子,点 x \text{x} x son x \text{son}_x sonx 连的边定义为重边,剩余的边定义为轻边;同时,我们把由若干条(可以是 0 0 0)重边首尾拼接而成的链叫做重链,那么整棵树就可以被剖分成若干条重链,而且每一个点一定存在于一条重链中。这意味着我们可以用重链将树完全剖分

step2:dfn 序


\qquad 在介绍树剖时,曾提到过树剖是利用 dfn \text{dfn} dfn 序加线段树来完成子树修改、查询和路径修改、查询。对于子树的操作,我们都知道子树内的点 dfn \text{dfn} dfn 序永远是连续的,也就是说对于子树的操作是可以轻松完成的。那么对于路径操作,怎么搞呢?

\qquad 既然上面提到重链可以将树完全剖分,那么我们不妨在 dfs \text{dfs} dfs 的时候优先递归重儿子,然后再递归轻儿子,这样我们不仅可以保证子树内 dfn \text{dfn} dfn 序连续,还可以保证一条重链上的点的 dfn \text{dfn} dfn 序连续。有了这个重要性质,我们便可以轻松做到对重链的修改。想到了这一点,那么路径修改也就很简单了:假设一条路径是从 x \text{x} x y \text{y} y,我们将路径拆成从 lca \text{lca} lca x \text{x} x,从 lca \text{lca} lca y \text{y} y 两条路径,然后我们分别从 x \text{x} x y \text{y} y 开始往 lca \text{lca} lca 跳重链,对于一条重链,我们先跳到这条链的一个端点,在 Θ ( log ⁡ n ) \Theta(\log n) Θ(logn) 的时间内修改,然后直接跳到这条链的另一个端点,接着往上跳别的重链,重复上述操作即可。这就是重链剖分操作的整体过程。

step3:时间复杂度分析


\qquad 上述操作看起来非常暴力,我们接下来来分析一下这一过程的时间复杂度。首先,这一过程的时间复杂度跟我们跳过的重链的条数有着密切关系,跟长度并无太大关系。我们不难发现,两条重链是会被一条轻边断开的。因此,我们只需分析出这一过程中会跳过多少轻边即可。这就要回到我们为什么要定义重儿子了。

\qquad 重儿子的子树大小是所有儿子中最大的,但是我们并不能得知重儿子子树大小的范围。对于一条链,重儿子子树大小可能达到 Θ ( n ) \Theta(n) Θ(n) 级别,但是对于菊花图,这一大小可能只有 1 1 1。但是,我们是可以得到轻儿子的子树大小范围的。轻儿子的子树大小一定不超过 sze x 2 \Large \frac{\text{sze}_x}{2} 2szex。这一点我们通过反证法很好证明。接着分析下去,我们发现每经过一条轻边,子树大小就会至少除以 2 2 2,那是不是就意味着我们最多只会经过 Θ ( log ⁡ n ) \Theta(\log n) Θ(logn) 条轻边呢?那么我们跳链的次数也是 Θ ( log ⁡ n ) \Theta(\log n) Θ(logn) 级别的,配上线段树,总体时间复杂度 Θ ( n log ⁡ 2 n ) \Theta(n\log^2 n) Θ(nlog2n),就能轻松解决掉子树操作、路径操作的问题了。

代码实现


求重儿子

void dfs_pre(int x, int fa) {
	sze[x] = 1, dep[x] = dep[fa] + 1, fat[x] = fa;//需要记录每个点的父亲,跳链时会用
	for(int i = head[x]; i; i = edge[i].lst) {
		int To = edge[i].to;
		if(To == fa) continue;
		dfs_pre(To, x);
		sze[x] += sze[To];
		if(sze[To] > sze[son[x]]) son[x] = To;//更新重儿子
	}
}

重链剖分


\qquad 这一段应该是树剖中最重要的一个 dfs \text{dfs} dfs 了。

void dfs(int x, int fa, int chain) {
	bel[x] = chain;//记录当前点所在的链的链头
	dfn[x] = ++ num;//记录dfn序
	v[num] = x;//记录dfn序为num的点是哪一个
	if(son[x]) dfs(son[x], x, chain);//重儿子跟自己在同一条链中
	for(int i = head[x]; i; i = edge[i].lst) {
		int To = edge[i].to;
		if(To == fa || To == son[x]) continue;
		dfs(To, x, To);//轻儿子跟自己不在一条链中,每个轻儿子都新开一条链
	}
}

各种操作


求 lca:
int lca(int x, int y) {
	while(bel[x] != bel[y]) {//只要链头不同,就让 链头 深度更大的点往上跳
		if(dep[bel[x]] > dep[bel[y]]) x = fat[bel[x]];
		else y = fat[bel[y]];
	}
	return dep[x] > dep[y] ? y : x;
}
路径修改:
void C(int x, int y, int z) {//C:change
	for(; bel[x] != bel[y]; x = fat[bel[x]]) change(1, dfn[bel[x]], dfn[x], z);
	change(1, dfn[y] + 1, dfn[x], z);
}

//main 中
int Lca = lca(u, v);
C(u, Lca, w), C(v, Lca, w);//将路径拆成两条,分别修改
路径查询:
int Q(int x, int y) {
	int maxx = 0;
	for(; bel[x] != bel[y]; x = fat[bel[x]]) maxx = max(maxx, query(1, dfn[bel[x]], dfn[x]));
	maxx = max(maxx, query(1, dfn[y] + 1, dfn[x]));
	return maxx;
}

// main 中
int Lca = lca(u, v);
printf("%d\n", max(Q(u, Lca), Q(v, Lca)));

例题推荐


\qquad [ZJOI2008] 树的统计 板子题

\qquad [HAOI2015] 树上操作 板子题

\qquad [NOI2015] 软件包管理器 板子题

\qquad 月下“毛景树” 板子题,但是边权转点权,有点小细节

\qquad QTREE - Query on a tree 也是边权转点权

\qquad [国家集训队] 旅游 树剖板子,主要考查线段树操作

\qquad [SDOI2011] 染色 处理颜色段时有一点点小细节

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值