主席树工作原理

主席树工作原理

如果只用“数值区间”的线段树是不能求出“区间第k大”的,因为它只能求出一个序列整体的“第k大”,而不能拓展到任意区间。然而有这样的一个性质:如果我们用“M(i,j,x,y)”表示原序列中 从第i个数开始到第j个数结束 的区间 满足“数值区间”[x,y]的元素的个数,那么就有:M ( i , j , x , y ) = M ( 1 , j , x , y ) - M ( 1 , i - 1 , x , y )

这就说明,我不用给每个区间建立线段树,只要给原序列的每个前缀建立一个线段树就能求得所有区间与“数集”的关系。也就是说我们可以建立“一群线段树”来维护这个性质:

主席树工作原理_第1张图片

这看起来是不是像一个立体的结构。不过要注意的是,这里的每一棵线段树都有MlgM个结点,一共有N棵线段树,所以说空间复杂度一定会炸,而且构建这么多完整的线段树时间也一定会炸。但是你会发现,其实这些线段树都是非常相似的,因为两个长度只差1的前缀之间只有一个不同的数,因此它与前一个线段树只有一条支路是不同呢?那么为什么不让这些线段树共用一部分树枝呢?这听起来很疯狂,不过却很可行。

主席树工作原理_第2张图片

我们可以先为“空前缀”建立一棵“空线段树”,里面的权值全是零。然后依次为每一个前缀建立线段树,与前一棵树相同的区间直接接到前一棵树的对应节点上,不同的部分再新申请结点。比如:当我构造当前前缀的线段树的[1,4]数值区间时,如果当前前缀比上一个前缀新增的那个数为3或4,就把当前结点的左子接到上一个线段树的[1,2]区间上;如果新增的那个数为1或2,就把当前结点的右子接到上一个节点的[3,4]区间上。然后再用同样的方法递归处理新添加的结点。这样除了空树以外,每一个前缀只需要lgM个结点用于建树,空间复杂度为O((N+M)lgM),比较可以接受。so 我们储存一个treeRoot数组,用treeRoot[i]表示第i个前缀所对应的线段树的根节点,以便于以treeRoot[i+1]为根的线段树的构建。

你可能感兴趣的:(ACM笔记-2串树)