[学习笔记]树链剖分

基本思想

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

一些定义

树链:树上的路径.

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

重儿子: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);
}

时间: 2024-12-19 19:35:28

[学习笔记]树链剖分的相关文章

树链剖分学习笔记

先让我们看一个题目 有一棵n个节点的树,树的每条边有个边权,有如下两种操作 1.修改一条边的边权 2.查询两点之间路径的权值 对于这种题目,可能有人会选择直接暴力,这很明显不行. 换一种思路,如果我们把树的每一条边拆下来,对他们进行编号,然后使用线段树来存储呢?使用线段树来对每条边的边权进行修改和查询是很方便的.于是这样我们就引出了树链剖分. 树链剖分其实就是把一棵树上的各个边拆开来进行处理,从而对树的整体进行划分. 将树划分为链,用数据结构来维护这些链,时间复杂度大致为O(log n) . 在

树链剖分 学习笔记

像我这么懒肯定不会有图辣 在GCZ大爷的安利下来学树剖,慢慢补吧... 前言 这几天边看<高级数据结构>,边看各路神犇的Blog,终于看懂一点点了.不得不说那本书作者的码风是真的垃圾. 树链剖分,是一种将树剖分成多条不相交的链的算法,并通过其他的数据结构来维护这些链上的信息. 最简单的例子就是LCA,假设现在有一棵退化成链的树.如果要求任意两点的LCA,因为他们在同一条链上的缘故,只需要判断一下两者的深度就行了.由此可见,在链上是比在树上更好操作的. 那么该怎么将一棵树剖分开来捏? 先搬出一堆

树链剖分(轻重链剖分)算法笔记[转]

仔细想想 自己第一次听说这个这个数据结构大概有两年半的时间了 然而一直不会. 不过现在再回头来看 发现其实也不是很麻烦 首先 在学树链剖分之前最好先把LCALCA 树形DPDP 以及dfsdfs序 这三个知识点学了 如果这三个知识点没掌握好的话 树链剖分难以理解也是当然的 ------------------------------------------------------------------------------------------- 树链剖分通常用于处理树的形态不变 但点权/

树链剖分学习记录

HDU  3966 基于点权 修改(增减)一条路径上的点  单结点查询 code: #pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 50010; struct Edge{ int to, next; }e

树链剖分学习&amp;BZOJ1036

题目传送门 树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组.SBT.SPLAY.线段树等)来维护每一条链. 以下是几种概念: 常见的路径剖分的方法是轻重树链剖分(启发式剖分) 将树中的边分为:轻边和重边 ?定义size(X)为以X为根的子树的节点个数. ?令V为U的儿子节点中size值最大的节点,那么边(U,V)被称为重边,树中重边之外的边被称为轻边. 性质:?轻边(U,V),size(V)<=size

HYSBZ 1036 【树链剖分】

思路: 裸裸的树链剖分.... 树链剖分就是把一棵树分成若干重链和轻链...然后保证形成的线段树上每条链是连续存储的.然后这样就能用线段树进行维护了. 但是每次一定要保证是在同一条链里边....思路就是这样.... 感觉最近越来越淡定了,题目卡住了也不怎么着急了,慢慢看代码...嗯...有点像学习的感觉了.... 明天青岛理工的邀请赛要好好玩玩... #include<stdio.h> #include<string.h> #include<algorithm> usi

树链剖分专题

学习了一下树链剖分,找了几个有意义的题目训练一下 前4题是基础训练, A.B是AOV树(点记录信息) C.D是AOE树(边记录信息) *注意一下poj好像是提交的代码包含/**/这样的注释会出问题 A.HDU 3966 题意:给你N个点M条边的一棵AOV树,有P次操作 操作分别是:‘I’:将C1到C2路径上的点增加K:     ‘D’:将C1到C2路径上的点减少K:     ‘Q’:问你C点上的权: 解:树链剖分基础题,需要注意的是杭电的服务器是windows的,代码中加入用下面这句话扩栈 #p

树链剖分小结

这两周在学树剖. 先扔个模板 有一类题目,要求实现一类在树上的操作,比如: 修改/求 树上某 节点/边权 的(最)值: 修改/求 树上某 节点/边权 及其子树上所有节点的(最)值: 修改/求 树上某两点路径间的 节点/边权 的(最)值: 乍一看似乎用线段树就可以实现,但是如果仔细想想,可以发现单凭线段树是无法解决的. 对于这类题目,常用的解决方法是树链剖分. 树链剖分(节选自starszys博客): 相关定义: 重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子. 轻儿子:v

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要