长链剖分 总结 【知识点】

附上自学博客链接
https://www.cnblogs.com/Khada-Jhin/p/9576403.html

概念:

长链剖分类似于重链剖分,只是选择链的标准:
不是 子树节点最多的儿子,而是 到叶节点的路径最大的子节点
(所以说是长链)

顶点:一条长链上深度最小的点


性质:
对于一个有n个节点的树,

  1. 每一个子节点的最长链长度不长于他的祖先节点 (显然。。)

  2. 所有长链长度之和等于 n(总结点数)
    证明:
    一个点也算一条长链,每一个点在且只在一条长链上

  3. 从根节点走到任何一个节点,最多 n \sqrt{n} n 条长链(这是个大约数)
    证明:
    把一条长链上的点抽象成一个点,(总贡献为1)
    所以,经过的每一个点都不在同一条长链上 ,那么走出的这条链长度为 k ,
    有:
    根节点一定有一个子节点,链长 ≥ k \ge k k
    根节点下一个点一定有一条子节点,链长 ≥ k − 1 \ge k-1 k1
    …以此类推
    所以走下去后,没有走过的点为 ∑ 1 k \sum_1^k 1k 约为 k 2 / 2 k^2/2 k2/2
    所以k最大大约不过 n \sqrt {n} n


应用:

例题传送门

询问一棵树上任意一个点第k个祖先

O(n*logn)预处理+ O(1)在线回答

两次dfs,两次都类似重链剖分的dfs,只是转换条件换成了链长

void dfs1(int u){
	len[u]=1;int v;
	for(int i=1;(1<<i)<=dep[u];++i){
		fa[u][i]=fa[fa[u][i-1]][i-1];
	}
	for(int i=head[u];i;i=e[i].nxt){
		v=e[i].v;if(v==fa[u][0])continue;
		fa[v][0]=u;dep[v]=dep[u]+1;
		dfs1(v);
		if(len[v]>mx[u])mx[u]=len[v],son[u]=v;
	}
	len[u]+=mx[u];
}
void dfs2(int u){
	int v;
	aft[top[u]].push_back(u);
	for(int i=head[u];i;i=e[i].nxt){
		v=e[i].v;if(v==fa[u][0])continue;
		top[v]=son[u]==v ? top[u] : v;dfs2(v);
	}
}

重点是询问环节,
维护:

我们对于每一条长链的顶端,维护两个 vector 数组
p r e [ i ] [ j ] : pre[i][j]: pre[i][j]: 从i开始向上第j个祖先(没错,暴力维护。。)
a f t [ i ] [ j ] : aft[i][j]: aft[i][j]: 从i开始向下长链上第j个后代

注意:
j ≥ 1 j \ge1 j1 && j ≤ l e n [ i ] j\le{len[i]} jlen[i]

每一个顶点只维护所在长链长度个
由于长链和等于 n(第二条性质) 所以两个维护复杂度都是 O(n)

再对 1-n(数字)维护一个 highbit(即二进制位上最靠左的1的位置,可以和lowbit对比理解)
(这个维护不会超过 O(n*log你) )

操作:

对于 询问 第 x 号节点 的 第 k 个祖先 ,我们把目标祖先设为fa,
分类讨论:
1.fa和x在一条长链上,即 d e p [ x ] − d e p [ t o p [ x ] ] ≥ k {dep[x]-dep[top[x]] } \ge k dep[x]dep[top[x]]k
直接用 t o p [ x ] top[x] top[x]aft 数组即可

2.fa 和 x不在一条长链上,即 d e p [ x ] − d e p [ t o p [ x ] ] &lt; k {dep[x]-dep[top[x]] } \lt k dep[x]dep[top[x]]<k
这个时候我们跳到x的 向上 2 h i g h b i t ( k ) 2^{highbit(k)} 2highbit(k)个祖先 ,设为 y
注意,剩下的 k − 2 h i g h b i t ( k ) &lt; 2 h i g h b i t ( k ) k-2^{highbit(k)} \lt2^{highbit(k)} k2highbit(k)<2highbit(k)

对于 y ,y所在长链长度一定 ≥ 2 h i g h b i t ( k ) \ge2^{highbit(k)} 2highbit(k) (根据第一条性质)
所以pre和aft都可以覆盖

对于y所在长链的顶端,即top[y]
如果在 fa上方,就和第一种情况相同
如果在 fa下方, 就用 top[y] 的 pre 数组 O(1)求即可

int get(int u,int k){
	if(k>=dep[u])return 0;
	if(dep[top[u]]<=dep[u]-k)
		return aft[top[u]][dep[u]-dep[top[u]]-k];
	
	u=fa[u][Log[high[k]]];
	k-=high[k];
	if(k>=len[u])return 0;
	return dep[u]-dep[top[u]]>=k ? aft[top[u]][dep[u]-dep[top[u]]-k] : pre[top[u]][k-(dep[u]-dep[top[u]])];
}

代码:

总的代码写的很丑。。这里就不给了,其他的维护自己yy一下都很简单。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值