线段树合并初探
线段树合并
线段树合并就是把两棵权值线段树给合并起来,复杂度\(O(nlogn)\)。
值得注意的是:是两棵线段树对应位置进行合并操作!
有两种写法:
inline int merge(int x,int y,int l,int r){
if(!x||!y)return x+y;
if(l==r){tr[x]+=tr[y];return x;}
int mid=(l+r)>>1;
ls[x]=merge(ls[x],ls[y],l,mid);rs[x]=merge(rs[x],rs[y],mid+1,r);
pushup(x);return x;
}
这种是把y合并到x上。但是这种合并过程中有可能会破坏x,y的结构。所以适合把询问都离线下来,一合并完就回答询问。
另一种写法是类似主席树,新开节点。这种不需要把询问离线,但是空间花费大。
inline int merge(int x,int y,int l,int r) {
if(!x||!y)return x+y;
int rt=++cnt;
if(l==r) {tr[rt]=tr[x]+tr[y];return rt;}
int mid=(l+r)>>1;
ls[rt]=merge(ls[x],ls[y],l,mid);rs[rt]=merge(rs[x],rs[y],mid+1,r);
pushup(rt);return rt;
}
例题
[POI2011]ROT-Tree Rotations
Link
前序遍历 根\(\rightarrow\)左\(\rightarrow\)右。所以逆序对无非左子树内部、右子树内部、跨越根节点三种。前两种递归计算,最后一种对两侧开权值线段树,线段树合并计算。
[Vani有约会]雨天的尾巴
Link
dfs时做完这个节点就要在rt[i]处取他的答案,不能在dfs整棵树之后再在rt[i]处去答案。因为把当前节点的线段树合并之后,再操作其祖先,可能会涉及当前节点线段树上值的变化。