树链剖分

关于链剖

链剖实质上是通过轻重链的划分,将树剖成一条条重链,首尾相连存进数据结构(线段树、树状数组、splay之类的),支持路径查询,修改等

如何实现?

定义以下几种东西

  • 重儿子 子树大小最大的儿子
  • 轻儿子 除重儿子其他的都是轻儿子
  • 重边 每个点连向它的重儿子的边
  • 轻边 连向轻儿子的边
  • 重链 重边组成的链

这里写图片描述

这里总共有5条重链,1-2-4-8、5、9、3-6-10、7

我们按照优先重链的 DFS 序重新标号,用 DFN[] 数组记录,例如 DFN[8]=4,DFN[7]=10

定义几个数组 top[],son[],deep[],size[] ,分别表示所在重链顶端,重儿子编号,深度,子树大小

我们可以一次 DFS 求出 son[],deep[],sizep[]
再一次 DFS 求出 top[],dfn[] ,这两个 DFS 非常简单,自己随便YY一下

然后就可以根据DFN的顺序依次丢进数据结构里面。

重点在于,如何路径修改和路径查询

一般的都是要做一个 LCA 的,然而链剖并不需要

设当前要修改的路径起点 u ,终点v

如果 u,v 在同一条重链上,即 top[u]=top[v] ,那么可以直接在数据结构上区间修改 dfn[u] dfn[v] 这一段。

如果 u,v 不在同一条重链上,需要比较 deep[top[u]],deep[top[v]]

不妨总是使 deep[top[u]]>=deep[top[v]] ,不是的话就交换

那么我们区间修改 dfn[top[u]] dfn[u] ,再把 u 跳到father[top[u]]

重复操作,直到 u=v

查询也是一样的。

复杂度是 O(Nlog2N)

下面有一个例题
[ZJOI2008][JZOJ2256][BZOJ1036]树的统计

代码见这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值