可持久化线段树

可持久化线段树就是支持历史版本的查询和修改的线段树。主席树就是可持久化线段树的一种

思想

如果正常情况下我们想要保留每个历史版本的话。那么假如有n次操作,就要搞n棵线段树。
但是我们发现,第i棵与第i-1棵线段树的大部分节点都是相同的,那么可不可以共用这些节点,从而减小时空复杂度呢。
首先我们要知道哪些节点是可以共用的,或者说哪些节点是不同的。
我们举个栗子,假如现在我们维护了一个长度为4的序列的区间和,假如现在线段树中1,2,3,4位置的数值都是1。就像这样

现在,我们要把3这个位置的数值+1,然后考虑哪些节点会受到影响。首先因为要保持历史版本,所以这个根A肯定不能动,所以我们就新建一个根R,然后因为3在A的右子树上,所以rt可以与A共用左子树B,然后新建一个右子树H,同样递归下去,对于新的右子树H,因为3在其左子树上,所以他可以与C共用右子树G,新建一个左子树。并且进行修改。

可以发现,这样操作每次都只是新建了log个节点。时空复杂度都是\((nlogn)\)

代码

void update(int &rt,int lst,int l,int r,int pos) {//rt为新建的节点,lst为上个状态当前节点
   rt = ++tot; //新建节点
   ls[rt] = ls[lst];rs[rt] = rs[lst];//先将左右子树都与上个状态共用,以后可能会修改。
   tree[rt] = tree[lst] + 1;//更新维护的值
   if(l == r) return;//到达叶子节点
   int mid = (l + r) >> 1;
   if(pos <= mid) update(ls[rt],ls[lst],l,mid,pos);//递归修改
   else update(rs[rt],rs[lst],mid + 1,r,pos);
}

查询的时候就是从要查询的版本的根开始和普通的线段树一样查询就好了。

例题

luogu3834
题解
bzoj3524
题解
bzoj2588
题解