树链剖分

8 篇文章 0 订阅
4 篇文章 0 订阅

前言

树链剖分:在一颗树上两点之间的路径的修改、求值。

原理

将一课树分成若干条链,将它们连起来,形成一条链,再用线段树等方法来维护、求值。


定义

在熟练剖分中,会使用到很多数组,这是它们的作用:

size[x]:在以x为根的子树中的节点的个数
son[x]:x的重儿子
deep[x]:x的深度
fa[x]:x的父亲
top[x]:x所在的链的顶部节点
aft[x]:x在合成的新链中的编号
bef[x]:x在树上的编号

那么重儿子就是某个节点的儿子中size[]值最大的节点,重边就是它们的之间的边,由重边连起来的链就是重链。那么其余的节点就是轻儿子轻边就是它们的之间的边。

性质

一、轻边(u,v),size(v)<=size(u)/2。
二、从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重路径。

实现

预处理

首先用一个dfs把size[]、son[]、deep[]、fa[]求出来。

int dg(int x)
{
    size[x]=1;
    int mx=0;
    for(int i=last[x];i;i=next[i])
    {
        if(to[i]!=fa[x])
        {
            fa[to[i]]=x;
            deep[to[i]]=deep[x]+1;
            dg(to[i]);
            size[x]+=size[to[i]];
            if(size[to[i]]>mx)
            {
                mx=size[to[i]];
                son[x]=to[i];
            }
        }
    }
}

再用一个dfs求top[]、aft[]、bef[]以及重链
从每一个轻儿子开始,沿着重边往下拉,直到拉到叶子节点为止。
最后,将所有的重链首尾相接,形成一条新的链,按照顺序,给每一个点一个新的编号。

int dg1(int x)
{
    aft[x]=++tot;
    d[tot]=x;
    if(top[x]==0)
    {
        top[x]=x;
    }
    if(son[x])
    {
        top[son[x]]=top[x];
        dg1(son[x]);
    }
    for(int i=last[x];i;i=next[i])
    {
        if(to[i]!=fa[x] && to[i]!=son[x])
        {
            dg1(to[i]);
        }
    }
}

修改、查询操作

当修改或查询u、v两个点的路径时
(1)当u、v在同一条重链上,即top[u]=top[v],显然直接修改aft[u]和aft[v]之间的值。
(2)当u、v不在同一条重链上,即top[u]<>top[v],如果deep[top[u]]>deep[top[v]],就修改aft[top[u]]和aft[u]之间的值,并将u赋值为fa[top[u]]。反之。
反复做(2)操作,直到变成(1)的情况。
时间复杂度O(NlogN)。


【ZJOI2008】树的统计
这道题可以用来练手,挺好滴。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值