课堂笔记 2021.7.27-2021.7.28

这篇博客介绍了动态开点线段树的实现,通过只在需要时创建节点来节省空间。同时讲解了线段树的合并操作,用于合并维护相同区间信息的两棵线段树。接着,文章阐述了树链剖分的概念,特别是重链剖分,用于高效解决树上路径修改问题。通过两次DFS将树划分为连续编号的链,简化问题处理。这些数据结构技术在解决复杂树形结构问题时具有重要意义。
摘要由CSDN通过智能技术生成

还在更新。

动态开点线段树

众所周知,一棵线段树会包含很多节点,但是有很多节点都用不上,这样就会造成空间的浪费,有什么办法解决空间浪费呢?就是当我们需要的时候再去开点,不预先建树。

void build(int &i,int l,int r,int num,int k){
	if(!i)
	i=++ecnt;
	if(l==r){
		t[i].v+=k;
		return ;	
	} 
	int mid=(l+r)/2;
	if(num<=mid)
	build(t[i].ls,l,mid,num,k);//由于 i 是传的 &i,所以这里如果没有左右儿子就会新建且传回来
	else
	build(t[i].rs,mid+1,r,num,k);
	t[i].v=t[t[i].ls].v+t[t[i].rs].v; 
}

线段树合并

线段树的“加法”,当我们有两棵线段树,其中有两个节点维护了相同区间的相同信息时,它们是可以合并的,这样以来,就可以把两棵线段树合并了,譬如说两个连通块,分别用线段树维护了连通块中每个点权出现次数,当它们联通之后,我们就需要合并这两棵线段树,得到新的连通块的信息,具体实现是把第二棵线段树合到第一棵,并且记录每个点所对应线段树的根,在合并过程中修改即可:


int merge(int x,int y,int l,int r){
	if(!x)
	return y;
	if(!y)
	return x;
	if(l==r){
		t[x].v+=t[y].v;//为什么只合并叶子节点?因为答案会push_up
		return x;
	}
	int mid=(l+r)/2;
	t[x].ls=merge(t[x].ls,t[y].ls,l,mid);
	t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r);
	t[x].v=t[t[x].ls].v+t[t[x].rs].v; 
	return x;
} 

树链剖分

回想一下,对于树上的修改问题,我们会通过什么方式求解?

dfs 序,欧拉序…

本质上我们是把树型结构转化为线性结构

想象一下,如果我们把一棵树剖成一条一条的链,对于解决树上路径修改问题,只需要一条链一条链的区间修改,不就很好吗?

于是树链剖分(下文简称树剖)因应而生,比较常用的是重链剖分,又叫轻重链剖分。

先来明确什么是一棵树的重量,一般来说,以 i i i 为根的子树大小就是 i i i 的重量。

重链剖分是这样的:对于每个节点,找到其重量最大的儿子节点,称之为重儿子,从父节点到重儿子的边称为重边,在草稿纸上画一画,很容易发现,多条重边经常是以一条链的形式出现,因此得到了重链的概念:我们称一条链为重链,当且仅当链上所有边都是重边。继续观察你的草稿纸,你会发现,重链之间夹杂了些许“零碎”的边,这就是轻边

结论1:在一棵有 n n n 个节点的树中所有轻边所指向的点(称为轻儿子)的重量不超过 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil 2n

证明:反证法,设存在这样的一个节点,其有一个轻儿子重量大于 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil 2n,根据对“重儿子”的定义,这个重儿子的重量也会大于 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil 2n,二者相加,这个节点的重量就会大于 n n n,显然在这样一棵树中不会有一个重量大于 n n n 的节点。

结论2:一棵有 n n n 个节点的树上的一条路径,至多经过 log ⁡ 2 n \log_2 n log2n 条重链

证明:我们从一条重链走到另一条重链的过程中,根据定义,显然会经过一条轻边,根据结论 1,显然最多经过 log ⁡ 2 n \log_2 n log2n 条轻边,而重链的数量和轻边数量理论上时同阶的。

这样以来我们就把一棵树划成若干条链了,对于每条链分别进行维护,但实际实现上是通过两次 dfs:

  • 第一次 dfs:
void dfs1(int x,int fa){
	dep[x]=dep[fa]+1; 
	cnt[x]=1;
	Fa[x]=fa;
	for(int i=h[x];i;i=e[i].nxt){
		int v=e[i].v;
		if(v==fa)
		continue;
		dfs1(v,x);
		cnt[x]+=cnt[v];
		if(cnt[v]>mc[x]){
			mc[x]=cnt[v];
			Son[x]=v;
		}
	}
	return ;
}
  • 第二次 dfs:
void dfs2(int x,int fa,int as){
   top[x]=as;
   nid[x]=++tot2;
   if(Son[x])
   dfs2(Son[x],x,as);
   for(int i=h[x];i;i=e[i].nxt){
   	int v=e[i].v;
   	if(Son[x]!=v&&v!=fa){
   		dfs2(v,x,v);
   	}
   }
}  

这样以来就成功的划分了链并且使得一条链上的编号是连续的了。

严格来说后面的部分不属于树剖,所以先鸽了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 对于北邮-2021-软件工程-期末复习.rar,其实际内容应该包含了北邮软件工程课程的期末复习相关资料。这些资料可能包括了课堂讲义、考试重点、习题解答、历年试卷等内容,有助于帮助学生复习和准备考试。此外,该压缩文件可能也包含了一些软件工程方面的技术文献、著作或工具等资源,供学生自行查阅和学习。 软件工程是计算机科学的一个重要分支,旨在研究关于软件的开发、管理和维护等方面的最佳实践和方法。在学习软件工程的过程中,需要不断运用各种工具和技术,例如需求分析、设计模式、测试驱动开发、代码版本管理等。因此,掌握软件工程技能对于未来从事软件开发的工程师和研究者来说都是非常重要的。 总之,北邮-2021-软件工程-期末复习.rar是一个包含了软件工程课程复习相关资料的压缩文件,对于学习软件工程的同学们来说,它是一份宝贵的资源。在复习过程中,我们需要认真审核其中的内容,结合课堂实际情况,制定个人化的复习计划,不断加强基础知识的掌握,并结合实际应用,提高自己的软件开发能力。 ### 回答2: 北邮-2021-软件工程-期末复习.rar文件是北邮2021年软件工程课程的期末复习资料压缩包。其中包含了软件工程课程相关的学习资料和复习材料。在这个压缩包中,可能包括课件、PPT、习题和答案、教材、参考书籍、课程大纲等等。这些材料都是为了帮助学生进行软件工程课程的有效学习和备考复习所准备的。 该压缩包的目的是为学生提供一个集中、整理好的资源库,方便学生在期末复习阶段能够迅速找到所需资料。通过使用该压缩包,学生可以更加高效地进行软件工程知识的巩固和回顾,更好地理解和掌握课程中的关键概念和技术。 对于学生而言,打开这个压缩包后,可以根据自己的需要选择具体的学习材料进行查阅和使用。同时,也可以根据课程大纲或者考试的重点,有针对性地进行复习,并通过做习题巩固知识。 总之,北邮-2021-软件工程-期末复习.rar是一份为学生提供软件工程课程复习资料的压缩包,通过使用其中的学习资料,学生可以更加高效地进行软件工程知识的复习和巩固,为期末考试做好准备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值