线段树合并和分裂

线段树合并

空间复杂度,一般是根据操作次数来计算的,或者按照题目的空间,算出最大开多少数组。

根据感性理解,线段树的深度是\(\lceil log_2n\rceil\)的,反正\(d = \lfloor log_2n\rfloor+1\)肯定够。

\(m\)次操作,注意这个操作不一定是原题中的询问,而是你对于线段树的操作次数,总共就要开\(O(md)\)个点。

比如模板题操作次数就是\(4m\)\(m\)为题目修改次数)。

然后就是这种写法:

int merge(int l,int r,int x,int y) {
	if (!x||!y) return x+y;
	int mid=l+r>>1;
	if (l==r) {
		tr[x].max+=tr[y].max;
		if (!tr[x].max) tr[x].id=0;
		else tr[x].id=l;
		return x;
	}
	tr[x].ls=merge(l,mid,tr[x].ls,tr[y].ls);
	tr[x].rs=merge(mid+1,r,tr[x].rs,tr[y].rs);
	pushup(x);
	return x;
}

是舍弃了原先的两棵线段树,此时只能保证x的信息是正确的,全部merge之后y的结构会改变。

但是本题中我们每次查的时候,只会查询根的值,这时为什么根的值也会变呢?

合理来说,根不会被挂到另外一个根上,所以应该是正确的。

对拍良久……终于发现如果\(u\)\(v\)的父亲,\(u\)没有被修改过,那么\(u\)的根就是空,merge之后\(u\)的根会直接变成\(v\)的根,于是根节点的值就被改变了(这个根变成了\(u\)的线段树,不是\(v\)的线段树了)……

好坑……

example

7 3
1 2
2 3
2 4
4 5
1 6
6 7
5 6 1
6 1 5
1 3 4

时间复杂度:每次merge只会遍历到在xy中都存在的节点和它们挂着的儿子,挂着的儿子和它们自身的数量同阶,可以认为我们将y删去了,所以每次的复杂度就是删去的y的个数。

因为一个数被删,一定要在merge的时候被遍历到,而删去之后就不会被遍历到了,所以一个点至多会被删去一次,而总点数是\(mlogn\)的,所以总的复杂度是\(O(mlogn)\)的。


线段树分裂

终于过了!!!

中间变量忘开long long寄了……

分裂类似FHQ Treap,可以有按值和按大小两种。

每次分讨一下,然后更新两棵树就行了。

每次split至多新增\(logn\)个节点,一开始有\(4n\)个节点,最多有\(n+mlogn\)个点,merge与上面分析不同的是,点的总数一直在增加,但是我们不管中间过程,无论如何删去的点数一定不超过插入的点数,所以总共删去的点数不超过\(n+mlogn\),其它操作都是显然\(mlogn\)的,总的复杂度\(O(mlogn)\)

空间也是\(n+mlogn\)

posted @ 2023-11-05 21:45  Zlc晨鑫  阅读(22)  评论(0)    收藏  举报