dfs序+树链剖分,超详细讲解+原理分析+模板(看不懂来打我)

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

dfs序+树链剖分

一. 树链剖分能解决什么问题?

在这里插入图片描述

二.树链剖分前置知识

1.dfs序

在这里插入图片描述

2. 时间戳

按照dfs第一次访问的顺序,给每一个节点标记上时间戳
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210707164442392.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1RoZVdheUZvckRyZWFt,size_16,color_FFFFFF,t_70

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,
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值