树链剖分【2024蓝桥杯0基础】-学习笔记

重链剖分的介绍

在没有特殊说明的情况下,树链剖分都是指重链剖分,⽤于将树分割成若⼲条链的形式,以维护树上路径的信息。可以⽤ ⼀些数据结构来维护。

  • 重链剖分可以将树上的任意⼀条路径划分为不超过 O ( log ⁡ N ) O(\log{N}) O(logN) 条连续的链,每条链上的深度互不相同,链上所有 点的 L C A LCA LCA 为链的⼀个端点,可以保证划分出的每条链上的节点 D F S DFS DFS 序连续。下⾯是⼀些相关的定义:
  • 重⼦节点:表示其⼦节点中⼦树最⼤的⼦结点。如果有多个⼦树最⼤的⼦结点,取其⼀。如果没有- - ⼦节点,就⽆重⼦ 节点。
  • 轻⼦节点:表示剩余的所有⼦结点,即除了重⼦节点以外所有的节点。
  • 重边:⽗节点到重⼦节点的边。
  • 轻边:⽗节点到其他轻⼦节点的边。
  • 重链:若⼲条⾸尾衔接的重边,其中落单的结点也当作重链。
  • 轻链:由多条轻边连接⽽成的路径。
    已知以下性质:
  • 在轻边 ( u , v ) (u,v) (u,v) 中, s i z e ( u ) ≤ s i z e ( u / 2 ) size(u) \leq size(u/2) size(u)size(u/2)
  • 从根到某⼀点的路径上,不超过 log ⁡ N \log{N} logN 条轻链和不超过 log ⁡ N \log{N} logN 条重链。
  • 叶节点没有重⼦节点,⾮叶⼦节点有且只有⼀个重⼦节点,轻⼦节点之后不⼀定全是轻⼉⼦。
  • 树上每个节点都属于且仅属于⼀条重链,并且所有的重链将整棵树完全剖分。

重链剖分的实现

进⾏两次 D F S DFS DFS 来实现重链剖分。
第⼀次 D F S DFS DFS 记录每个节点的⽗节点,深度,⼦树⼤⼩,重⼦节点。刚开始搜到节点 u u u 时以 u u u 为根节点的⼦ 树⾥⾯只有 u u u ⼀个节点。当搜到与 u u u 连向的节点 v v v 时,需要判断⼀下 v v v 有没有被搜到过,如果没有,那么 记录⼀下⽗亲和 v v v 的深度,然后继续往下搜,⼀直搜到叶⼦节点为⽌。搜索完之后回溯,更新以 u u u 为根的⼦树的⼤ ⼩。根据重⼦节点的定义,如果以 u u u 的⼦树中,以 v v v 为根的⼦树节点多,那就更新⼀下 u u u 的重⼉⼦为 v v v。 代码如下:

def dfs1(u, depth):
    global sz
    global son
    dep[u] = depth
    sz[u] = 1
    for x in attr[u]:
        if x == fa[u]:
            continue
        fa[x] = u
        dfs1(x, depth + 1)
        sz[u] += sz[x]
        if not son[u] or sz[x] > sz[son[u]]:
            son[u] = x

第⼆次 D F S DFS DFS 记录节点 u u u 所在链的链顶 t t t,重边优先遍历时的 D F S DFS DFS 序,以及 D F S DFS DFS 序下当前点的点值和 题⽬中给出的节点 u u u 的点值。在求出来重⼦节点后,轻⼦节点需要被连成链,则需要让链上的点连续。从根开始 D F S DFS DFS,如果有重⼦节点就先⾛重⼦节点,搜到叶⼦节点后回溯回去再去搜轻⼦节点。代码如下:

def dfs2(u, tp):
    global ts
    global top
    global dfn
    global nw
    top[u] = tp
    dfn[u] = ts
    nw[ts] = w[u]
    ts += 1
    if not son[u]:
        return
    dfs2(son[u], tp)
    for x in attr[u]:
        if x == fa[u] or x == son[u]:
            continue
        dfs2(x, x)

重链剖分的基本操作

重链剖分⽀持修改和查询两种操作。因为重链剖分需要维护的是链,并且链上的 D F S DFS DFS 序连续,所以可以直接取节点 u u u 所在链的链顶 t t t 到它⾃⼰这⼀段进⾏修改或者查询,结束之后将当前节点跳到 t t t 的⽗节点,因为 t t t 已经 被查询或修改过了,跳到上⼀个节点可以防⽌重复操作。⼀般情况下,我们选取链顶较深的结点进⾏查询或修改,然后在往 上跳,这样可以避免死循环。下⾯给出代码:

u, v, k = op[1:]
while top[u] != top[v]:
    if dep[top[u]] < dep[top[v]]:
        u, v = v, u
    modify(1, dfn[top[u]], dfn[u], k)
    u = fa[top[u]]
if dep[u] < dep[v]:
    u, v = v, u
modify(1, dfn[v], dfn[u], k)
  • 35
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值