学习笔记——线段树合并

前言#

好像机房人人都会这个东西,就我不会,只能爬去学一下。。。

这是啥#

好像是个挺没用的东西,感觉主要处理树上数数什么的问题,都可以用 dsu 神器来替代。但也有的问题不能用 dsu 的,比如这题

来考虑这样一件事,就是说,我们有的时候希望对于每个点都维护一棵线段树,并且希望能够实现一些鬼畜的♂操♂作♂,这时候需要用到线段树合并。

但是我们考虑这样一件事就是说,由于是合并,那么在合并之前必然是有很多的线段树,所以传统的线段树肯定是没法用的。于是——

动态开点线段树#

这个东西还是比较简单的,就在插入元素的时候不采用堆式建树,而是采用记录左右儿子,然后在插入的时候访问到一个节点才会去开这个点的空间,这样就可以开下空间了。

注意一点是:动态开点的空间要开 20 倍以上,不然一开始空间会不太够。

void ins(int &i,int l,int r,int v){
	if(l>r) return;if(!i) i=++tot;
	tr[i].l=l;tr[i].r=r;
	if(l==r){
		//加上信息 
		return;
	}
	int mid=l+r>>1;
	if(v<=mid) ins(ls[i],l,mid,v);
	else ins(rs[i],mid+1,r,v);
	pushup(i);
}

线段树合并#

由于这个空间比较玄学,我找了好多地方,总结:空间一般能开多大开多大,一般开到 O(n(logn+1)) 就可以了。

为什么是这个东东?

由于我们是动态开点,那么初始的时候每个节点上的线段树空间是 log 的,由于线段树两倍的尿性,我们向上取整。

而合并的时候相当于把两棵线段树叠在一起,所以总空间就是 nlogn,但是又考虑到线段树两倍的尿性,我们加个 1

下面是代码。

int merge(int a,int b){
	if(!a) return b;
	if(!b) return a;
	if(tr[a].l==tr[a].r){
		//按需合并信息
		return a;
	}
	ls[a]=merge(ls[a],ls[b]);
	rs[a]=merge(rs[a],rs[b]);
	pushup(a);return a;
}

习题#

我们掏出这个题单

发现最后只询问一次,不需要树剖,直接树上差分。但是发现需要维护很多不同的颜色,所以考虑对每个节点维护一个线段树,然后向上做的时候合并就可以了。

record

好像 DSU 能艹,但是我们在学线段树合并,坚决不用 DSU!

套路一样,权值线段树合并就可以了。

record

挺牛逼题,考虑到子树内你无论怎么换,是不会影响到子树外的,所以贪心就可以了。

对每个节点维护一棵权值线段树,然后合并之前,先算一下左儿子的右区间中的数和右儿子左区间中的数,这些可以两两组成逆序对。同样的,由于可以交换,所以我们对于左儿子的左区间和右儿子的右区间也要算,然后取个较小的。递归继续合并。

record

这里是 DSU 水的题,实在不想用线段树合并

题目大致是求对于每个询问求一个点,有多少个点与其拥有共同的 k 级祖先(除本身)。

哦哟很可以 DSU。但是我们在学线段树合并,坚决不用 DSU!

艹不太会用线段树合并做,先用 DSU 过了再说。。。

哦哟苦痛,DSU 倍增卡空间,不会长剖,于是看了讨论区,发现这题并不需要 DSU 或者线段树合并,只需要一个奇怪的 trick,在深度遍历一个子树的前后你差分求出深度为 k 的节点个数就行了。诶但是问题是还是要倍增啊……

djwj233 给出了维护递归栈的做法。吊了,直接查,空间是 O(n) 的。

record

牛了,zzmg 题。。。考虑把题意翻译一下:

每次给定一个 p,k。求满足条件的二元组 (b,c) 的个数。

  1. p,b 都是 c 的祖先
  2. p,b 之间距离不超过 k

然后我们发现,由于 p 是固定的,所以如果 bp 上方,那么 c 的选法就是 sizp1。但是如果 bp 的下方——

这样 c 选的方案数就与 b 有关了,具体地,就是 sizb1。而 b 的选法就是在 p 的子树中并且深度差不超过 k 的节点数。

那第一种可以直接算,第二种似乎可以 DSU……啊不,线段树合并!个鬼啊,写 DSU 去了。

record

诶哟李超树,不会,先咕了。

哦 Ynoi,准备苦痛了。

posted @   ZCETHAN  阅读(75)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示
主题色彩