树链剖分详解

前言

先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构来维护每一条链,许多比赛都会用到这个方法。

相关概念

重结点:子树结点数目最多的结点;
轻节点:父亲节点中除了重结点以外的结点;
重边:父亲结点和重结点连成的边;
轻边:父亲节点和轻节点连成的边;
重链:由多条重边连接而成的路径;
轻链:由多条轻边连接而成的路径;

相关数组定义

s[x] 保存以x为根的子树节点个数
top[x] 保存当前节点所在链的顶端节点
son[x] 保存重儿子
dep[x] 保存结点x的深度值
fa[x] 保存结点x的父亲节点
id[x] 保存树中每个节点剖分以后的新编号(DFS的执行顺序)
rank[x] 保存当前节点在树中的位置

相关性质

1、ž轻边(U,V),size(V)<=size(U)/2。
2、ž从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重路径。
因为性质2,树链剖分就有了一个很不错的时间复杂度。

具体实现

1、第一遍dfs求出所以节点的s,son,dep,fa。

void dfs(int x)
{
    int t=0;si[x]=1;
    for(int i=lst[x];i;i=nxt[i])
        if(to[i]!=fa[x])
        {
            dep[to[i]]=dep[x]+1;
            fa[to[i]]=x;
            dfs(to[i]);
            si[x]+=si[to[i]];
            if(si[to[i]]>t)t=si[to[i]],son[x]=to[i];
        }
}

2、第二遍dfs,
按照重儿子优先的顺序,跟所以点重新编号,并求出每个点所在重链的链顶。

void dfs_(int x,int t)
{
    id[x]=++now;
    rank[now]=x;
    top[x]=t;
    if(son[x])dfs_(son[x],t);
    for(int i=lst[x];i;i=nxt[i])
        if(to[i]!=fa[x] && to[i]!=son[x])
            dfs_(to[i],to[i]);
}

两遍dfs之后,树链剖分就可了,剩下就是用数据结构去维护了。
在重新的编号中,重链是连续的一段。
查询两个点之间的信息时,先查找lca。
具体做法:
判断两个点是否在同一条重链上,如果是,lca就是在这条重链上了。
如果不是,选择深度(链顶)较大的重链向上跳一条重链(类似求lca)。
不断重复上面的操作,直到找到lca为止。

每向上跳一次,都记录下需要查询的信息。
因为从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重路径,那么查询的复杂度就是 O(logn)

经典例题

【ZJOI2008】树的统计

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值