树链剖分(轻/重链剖分学习笔记)

Posted lyz09-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分(轻/重链剖分学习笔记)相关的知识,希望对你有一定的参考价值。

个人树链剖分是一个暴力数据结构,也就是它的本质就是暴力,只不过优化了一下而已。 树链剖分一般用于维护树上两点之间或子树中的权值。算是树上问题中较为基础的一个算法。

前置知识:LCA,树上dp。

前言

个人树链剖分是一个暴力数据结构,也就是它的本质就是暴力,只不过优化了一下而已。

树链剖分一般用于维护树上两点之间或子树中的权值。算是树上问题中较为基础的一个算法。

定义

轻/重链

对于树上的某个节点的所有子树中,如果这个儿子的所在的子树是这些子树中最大的(节点个数最多的),则称这个儿子为重儿子,其余的儿子则为轻儿子。除叶子节点外,所有节点都有恰好有一个重儿子,子树大小相同则取任意一个。

在这个树上连向重儿子的边叫做重边,其余的边叫做轻边。一段连续的重边连成的链叫做重链,下面给出一张图来解释一下:

在这张图中,红色的节点为重儿子,黑色的节点为轻儿子,一段连续和红色的边即为重链。\\(3,4,5,6\\) 为重儿子,\\(2,7,8\\) 为轻儿子,\\(1\\) 为根节点(一般标记为轻儿子)。两个重链分别为 \\(2\\rightarrow 4\\rightarrow 5\\) 为一条重链,\\(1\\rightarrow 3\\rightarrow 6\\) 为另一条重链。一条重链总是在叶子节点结束(可以自己证明一下)。

[学习笔记]树链剖分

基本思想

树链剖分一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每条边属于且只属于一条链,然后再通过数据结构来维护每一条链。

一些定义

树链:树上的路径.

剖分:把路径分类为重链和轻链.

重儿子:u的子节点中siz[v]值最大的v.

轻儿子:u的其它子节点.

重边:点u与其重儿子的连边.

轻边:点u与其轻儿子的连边.

重链:由重边连成的路径.

轻链:轻边.

性质

  1. 如果(u,v)为轻边,则siz[v]$\times$2<siz[u].
  2. 从根到某一点的路径上轻链、重链的个数都不大于log$\;$n.
  3. 树剖序其实也可以是dfs序的一种.

实现

一些变量:

f[u]表示u的父亲.

siz[u]表示以u为根的子树的大小.

dep[u]表示u的深度(根深度为1).

top[u]表示u所在的链的顶端节点.

son[u]表示与u的重儿子.

重标号:

p[u]:重标号后u的编号.

dfs序:dfs的时候先走重边.

这样可以使得重边的编号是连续的,方便维护.

  • 用两遍dfs求出所需的所有变量以及重标号.
int f[N],p[N],dep[N],siz[N],son[N],top[N];
/*top[u]:u所在的链的顶端节点,son[u]:u的重儿子*/
inline void dfs1(int u){
    int m=0;siz[u]=1;
    for(int i=g[u];i;i=e[i].nxt)
        if(!dep[e[i].to]){
            f[e[i].to]=u;
            dep[e[i].to]=dep[u]+1;
            dfs1(e[i].to);
            siz[u]+=siz[e[i].to];
            if(siz[e[i].to]>m){
                son[u]=e[i].to;
                m=siz[e[i].to];
            }
        }
}
inline void dfs2(int u,int tp){
    top[u]=tp;p[u]=++cnt;ww[cnt]=w[u];
    if(son[u]) dfs2(son[u],tp);
    for(int i=g[u];i;i=e[i].nxt){
        if(e[i].to!=f[u]&&e[i].to!=son[u])
            dfs2(e[i].to,e[i].to);
    }
}
  • 访问修改(u,v):

类似倍增的走法,每次将深度大的往上移,直到u,v属于同一条链.

inline int sum(int x,int y){
    int ret=0,t;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]){
            t=x;x=y;y=t;
        }
        ret+=ask(1,p[top[x]],p[x]);
        x=f[top[x]];
    }
    if(p[x]>p[y]){
        t=x;x=y;y=t;
    }
    ret+=ask(1,p[x],p[y]);
    return ret; 
}
inline void change(int x,int y,int k){
    int t;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]){
            t=x;x=y;y=t;
        }
        cover(1,p[top[x]],p[x],k);
        x=f[top[x]];
    }
    if(p[x]>p[y]){
        t=x;x=y;y=t;
    }
    cover(1,p[x],p[y],k);
}

以上是关于树链剖分(轻/重链剖分学习笔记)的主要内容,如果未能解决你的问题,请参考以下文章

树链剖分 入门

树链剖分—学习笔记

树链剖分

树链剖分

树链剖分求LCA

树链剖分 [模板]最近公共祖先LCA