「学习笔记」树链剖分

轻重链剖分

树链?剖分?

树链剖分,顾名思义是把一棵树剖分成若干条链。
轻重链剖分满足每个结点属于且只属于一条重链。

首先引出概念:

名称概念
重儿子父结点的子树结点数量最多的子结点
轻儿子父结点中除了重儿子以外的儿子
重边连接父结点和重儿子的边
轻边连接父结点和轻儿子的边
重链若干条重边连接而成的路径
轻链若干条轻边连接而成的路径

1
现在有一棵树,对于该树,从根结点开始寻找重儿子,并且其重儿子也向下寻找重儿子,最终这些重边连成一条重链。而根结点其他的儿子(即轻儿子)也各自寻找自己的重儿子,使得整棵树分成若干条重链。
2
3
实现
剖分的过程由两个dfs实现。

名称解释
fa[u]结点u的父结点
dep[u]结点u的深度
size[u]以结点u为根的子树的结点数目
son[u]结点u的重儿子
rk[u]结点u的dfs标号在树中所对应的结点
top[u]结点u所在链的顶端结点
id[u]结点u的dfs序

1. \large\mathcal{1.} 1.
标记每个结点的 father,dep,size,并找出每个结点的重儿子。

void dfs1(int u , int father , int depth) {
	fa[u] = father;//记录父结点 
	dep[u] = depth;//记录深度
	size[u] = 1;//将当前子树size标记为1
	
	for (int i = head[u]; i; i = edge[i].next) { 
		if (edge[i].to != father) {
			dfs1(edge[i].to , u , depth+1);//深度++ 
			
			size[u] += size[edge[i].to];//更新父结点的 size 
			
			if (size[edge[i].to] > size[son[u]]) { 
				son[u]=edge[i].to;//选取size最大的子结点作为重儿子 
			} 
		}
	}
}

调用:

dfs1(root , 0 , 1)

2. \large\mathcal{2.} 2.
连接重链,标记dfs序,处理出数组top,id,rk

void dfs2(int u , int t) {
	top[u] = t;//通过标记当前链的链首来连接重链
	id[u] = ++cnt;//记录dfs序
	rk[cnt] = u;//记录dfs序指向的节点
	
	if (!son[u]) return;
	
	dfs2(son[u] , u);//优先延伸重链,使同一条重链上的dfs序连续
	
	for (int i = head[u]; i; i = edge[i].next) {
		if (edge[i].to != son[u] && fa[u]) {
			dfs2(edge[i].to , edge[i].to);
		}
	}
}

时间复杂度

树链剖分的两个性质:

  • 如果(u, v)是一条轻边,那么 s i z e ( v ) < s i z e ( u ) ÷ 2 \mathcal{size(v)<size(u)÷2} size(v)<size(u)÷2;
  • 从根结点到任意结点的路所经过的轻重链的个数必定都小于 log ⁡ n \log n logn.

可得,树链剖分的时间复杂度为 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n) 或者写做…… O ( n ( log ⁡ n ) 2 ) O(n(\log n)^2) O(n(logn)2)
显然是一种优秀的算法。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值