线段树合并
Posted sktt1faker
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树合并相关的知识,希望对你有一定的参考价值。
(鸽 王 归 来)
算法简介
线段树合并可以将2个权值线段树合并为一个。
实现
很简单,我们的操作如下:
- 2棵线段树都有的节点,把它们的值合并。
- 只有一颗线段树有节点,那么合并出来的线段树节点的值就是这个节点的值。
- 依次递归下去搞定一切。
一般来说,如果不需要用合并前的线段树信息,我们就可以卡一下空间,把一棵线段树合并到另一个上:
int merge(int x,int y,int l,int r)
{
if(!x||!y) return x+y;
//这里写值的合并;
int mid=(l+r)>>1;
ls[x]=merge(ls[x],ls[y],l,mid);
rs[x]=merge(rs[x],rs[y],mid+1,r);
return x;
}
需要用合并前的线段树的话,空间得开2倍。这时候写个垃圾回收可以卡空间。
应用
看几道例题吧。
-
[Vani有约会]雨天的尾巴 /【模板】线段树合并
板子题。就差分一下再合并。 -
[POI2011]ROT-Tree Rotations
首先对于一个权值线段树,它的逆序对数是很好维护的。对于某一个节点,它的子节点已经维护好了权值没有跨过(mid)的逆序对,那么对于跨过(mid)的逆序对,只需要把子节点的权值数量乘起来就行了。
那么我们现在,要用树上一个点的2个儿子更新它自己的信息,就是要求把2个儿子对应的叶子节点区间合并了。那么就用线段树合并,合并时用上述思路计算2种情况跨过(mid)的逆序对数量。
- [省选联考 2020 A 卷] 树
好家伙,新鲜出炉的省选题。
注意:这里我们数一个数的位,都是从后往前数。
我们的思路很清晰:用01trie维护每个点。对于某一个点,我们把子树的01trie合并起来(trie合并和线段树合并一个道理,代码几乎一样),再整体加(1),最后插入该点的权值。现在要解决的问题是如何整体(+1)。
我们思考对一个(2)进制数(+1)的后果,考虑当前位置已经处理完了,要处理下一位(也就是要处理trie的子节点),那么假设下一位是(0),(+1)后就变成(1);假设下一位是(1),(+1)后就变成(0)。也就是说下一位(01)取反了,于是直接交换左右子树。
同时如果下一位原先是(1),现在变成了(0),那我们还要考虑进位,于是递归到左子树(因为下一位已经变成(0)了)继续加。假如下一位现在变成(0),但左子树没有右儿子,那么对于左子树还要新建一个右儿子(相当于你还要添一位)。
于是这道神奇的题就解决了。
以上是关于线段树合并的主要内容,如果未能解决你的问题,请参考以下文章