[线段树系列 #4] 线段树分裂
[线段树系列 #4] 线段树分裂
简单介绍
线段树分裂,可以简单理解为把所需的链单独取出来,即为合并的逆操作
线段树分裂只适用于有序的序列,无序的序列是没有意义的,常用在动态开点的权值线段树。
思路概述
一颗区间为 \([1,n]\) 的线段树中分裂出 \([l,r]\),并建一颗新树
从 1 号结点开始递归分裂,当节点不存在或者代表的区间与 \([l,r]\) 没有交集时直接回溯。
当与 \([l,r]\) 有交集时直接开一个新结点。
当被包含于 \([l,r]\) 时,需要将当前结点直接接到新的树下面,并与旧父节点断开
关于复杂度,易得被断开的边为 \(\log n\) 条,所以每次分裂都为 \(O(\log n)\)
具体实现
void split(ll x, ll &y, ll k){ // 原根为x, 移至y
if(x == 0) return;
y = newnode();
ll v = t[t[x].l].val;
if(k > v) split(t[x].r, t[y].r, k-v);
else swap(t[x].r, t[y].r);
if(k < v) split(t[x].l, t[y].l, k);
t[y].val = t[x].val-k;
t[x].val = k;
}
应用
附录:一篇写的很好的博客链接