标签:自己 lang 代码 逆序对数 新建 ons 要求 tar 节点
(鸽 王 归 来)
线段树合并可以将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\)的逆序对数量。
注意:这里我们数一个数的位,都是从后往前数。
我们的思路很清晰:用01trie维护每个点。对于某一个点,我们把子树的01trie合并起来(trie合并和线段树合并一个道理,代码几乎一样),再整体加\(1\),最后插入该点的权值。现在要解决的问题是如何整体\(+1\)。
我们思考对一个\(2\)进制数\(+1\)的后果,考虑当前位置已经处理完了,要处理下一位(也就是要处理trie的子节点),那么假设下一位是\(0\),\(+1\)后就变成\(1\);假设下一位是\(1\),\(+1\)后就变成\(0\)。也就是说下一位\(01\)取反了,于是直接交换左右子树。
同时如果下一位原先是\(1\),现在变成了\(0\),那我们还要考虑进位,于是递归到左子树(因为下一位已经变成\(0\)了)继续加。假如下一位现在变成\(0\),但左子树没有右儿子,那么对于左子树还要新建一个右儿子(相当于你还要添一位)。
于是这道神奇的题就解决了。
标签:自己 lang 代码 逆序对数 新建 ons 要求 tar 节点
原文地址:https://www.cnblogs.com/SKTT1Faker/p/13232277.html