dfs序+树链剖分,超详细讲解+原理分析+模板(看不懂来打我
dfs序+树链剖分
一. 树链剖分能解决什么问题?

二.树链剖分前置知识
1.dfs序

2. 时间戳
按照dfs第一次访问的顺序,给每一个节点标记上时间戳

3.dfs序和时间戳有什么用处





开始剖分,跑第二次dfs的作用,也即dfs1和dfs2

于是你需要这些数组来记录
struct Edge{
int v;//节点编号
int next;
}edge[Maxn*2];//注意开二倍,因为是无相边
int head[Maxn];
int fa[Maxn];//记录父节点
int dep[Maxn];//记录节点深度
int son[Maxn];//记录重儿子
int siz[Maxn];//记录以该节点为根节点的树的大小(节点个数包括根节点)
int top[Maxn];//每一个节点所属重链的根节点
int dfn[Maxn];//每一个节点的时间戳
int w[Maxn];//dfs序后节点的权值,用线段树维护
int tim = 0;//时间戳计数器
int cnt = 0;
int v[Maxn];//存放所有节点的权值
int sum[Maxn*4];//线段树区间数组维护w[]区间和
int lazy[Maxn*4];//维护区间加的延迟数据,以便于延迟下方
int lpos[Maxn*4],rpos[Maxn*4];//线段树区间的左右端点
链式前向星存图:
/*链式前向星建图,无向边*/
void build(int u,int v){
edge[++cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt;
edge[++cnt].v = u;
edge[cnt].next = head[v];
head[v] = cnt;
}
dfs1
/*报一遍dfs记录重儿子,节点深度,以及树的大小*/
void dfs1(int u,int f){
fa[u] = f;
dep[u] = dep[f]+1;
siz[u] = 1;
int maxsonsize = -1;//记录重儿子的大小
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;//与u有边的节点
if(v==f) continue;//如果v时u的父亲,直接跳过
/*否则就是u的儿子,dfs下去*/
dfs1(v,u);
siz[u]+=siz[v];
/*更新u的重儿子*/
if(siz[v]>maxsonsize){
maxsonsize = siz[v];
son[u] = v;
}
}
}
dfs2
/*再跑一边dfs,完成树链剖分*/
void dfs2(int u,int t){
dfn[u] = ++tim;//dfs序
top[u] = t;//u所属重链的祖先节点
w[tim] = v[u];//dfs序后的节点权值
/*没有重儿子,代表时根节点,直接return*/
if(!son[u]) return ;
dfs2(son[u],t);
for(int i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].v;
//如果v时u的父节点,或者v时u的重儿子(已经遍历过了)
if(v==fa[u]||v==son[u]) continue;
/*否则以该节点为新的重链的祖先,继续dfs序*/
else dfs2(v,v);
}
return ;
}
线段树的区间修改和查询,对链进行操作




操作1

操作2
和操作1有一点不同,把modif()改为query()即可

三.树链剖分练习题
1.模板题
AC代码:
/*轻重链剖分/树链剖分*/
#include<bits/stdc++.h>
#define ls dep<<1
#define rs dep<<1|1
using namespace std;
const int Maxn = 1e5+10;
int N,

本文详细介绍了树链剖分的原理,涉及dfs序、时间戳在链式前向星和线段树中的运用,以及如何解决区间修改、查询问题。通过模板题和LCA问题实例,配合视频教学,帮助读者深入理解并掌握这一技术。
最低0.47元/天 解锁文章
509

被折叠的 条评论
为什么被折叠?



