算法笔记——树链剖分

树链剖分是一种用于高效处理树结构的算法,它将树分解为若干条重链,便于进行单点修改、区间修改、区间查询等操作。通过两次深度优先搜索进行预处理,维护节点的深度、父节点、子树大小和重儿子信息。在处理LCA(最近公共祖先)问题时,树链剖分能快速定位节点并进行转换。该算法常与线段树结合,用于处理路径上的区间查询和修改。
摘要由CSDN通过智能技术生成

        树链剖分的思想是通过轻重链剖分将树分为多条链,保证每个节点都属于且只属于一条链。树链剖分是轻重链剖分,节点到重儿子(子树节点数最多的儿子)之间的路径为重链。每条重链都相当于一段区间,把所有重链收尾相接组成一个线性节点序列,再通过数据结构(如树状数组,SBT,伸展树,线段树等)来维护即可。

        若size[u]表示以u为根的子树的节点个数,则在u的所有儿子中,size最大的儿子就是重儿子,而u的其他儿子都是轻儿子,当前节点与其重儿子之间的边就是重边,多条重边相连为一条重链。

重要性质

(1)若v是轻儿子,uv的父节点,则size[v]\leqslant \frac{size[u]}{2}

(2)从根到某一点路径上,不超过log2n条重链,不超过log2n条轻边。

树链剖分支持以下操作

(1)单点修改:修改一个点的权值

(2)区间修改:修改节点u到v路径上节点的权值

(3)区间最值查询:查询节点u到v路径上节点的最值

(4)区间和查询:查询节点u到v路径上节点的和值。

预处理

树链剖分可以采用两次深度优先搜索实现

第1次深度优先搜索维护4个信息:

(1)dep[x]:x的深度

(2)fa[x]:x的父节点

(3)size[x]:以x为根的子树的节点数

(4)son[x]:x的重儿子,x-son[x]为重边

第2次深度优先搜索以优先走重边的原则,维护3个信息:

(1)top[x]:x所在的重链上的顶端节编号(重链上深度最小的节点)

(2)id[x]:x在节点序列中的位置下标。

(3)rev[x]:树链剖分后节点序列中第x个位置的节点。

id[]rev[]互逆。

预处理时间复杂度为O(n)

求解LCA问题

对于LCA问题,点和边均没有权值,因此无需维护线段树来实现。先进行树链剖分预处理。

void dfs1(int x)
{
    size[x]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==fa[x])    continue;
        dep[y]=dep[x]+1;
        fa[y]=x;
        dfs1(y,x);
        size[x]+=size[y];
        if(size[y]>size[son[x]])    son[x]=y;
    }
}

void dfs2(int x,int t)
{
    id[x]=++cnt;
    rev[cnt]=x;
    top[x]=t;
    if(!son[x]) return;
    dfs2(son[x],t);//优先重儿子
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==fa[x] || y==son[x])    continue;
        dfs2(y,y);//新的重链
    }

}

树上的任意一对节点只存在两种情况:①在同一重链上(top[x]=top[y])②不在同一条重链上

        对于第①种情况,LCA(x,y)就是x,y中深度较小的节点。

        对于第②种情况,只要想办法将x,y两点转移到同一条重链上即可。首先求出x,y所在重链的顶端节点top[x],top[y],将其顶端节点深度大的节点上移(转移到另一条重链),知道x,y在同一条重链上,再用第①种情况中的方法求解即可。

int LCA(int x,int y)
{
    while(top[x]!=top[y])
        if(dep[top[x]]>dep[top[y]])
            x=fa[top[x]];
        else
            y=fa[top[y]];
    return dep[x]<dep[y]?x:y;
}

树链剖分与线段树

若在树上进行点更新、区间更新、区间查询等操作,可以使用线段树来维护和处理。

轻重链的剖分方式决定了一个节点的子树节点在节点序列中是连续的。

查询节点x到y路径上节点权值的最值与和值的方法如下

(1)若u,v在同一重链,则在线段树上查询其对应的下标区间[id[x],id[y]]即可。

(2)若u,v不再同一条重链上,则一边查询,一边将u,v向同一条重链上移,然后采用上面的方法处理。对于顶端节点深度大的节点,先查询其到顶端节点的区间,然后一边上移一边查询,直到上移到同一条重链上,再查询在同一条重链上的区间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值