Sqrt Tree
引入
给你一个长度为 n 的序列
Sqrt Tree 可以在
解释
序列分块
首先我们把整个序列分成
块内的前缀区间询问 块内的后缀区间询问- 维护一个额外的数组
表示第 个块到第 个块的区间答案。
举个例子,假设
首先我们将序列分成三块,变成了
那么每一块的前缀区间答案和后缀区间答案分别为
(对于
显然我们可以在
构建一棵树
容易想到我们在每个块内递归地构造上述结构以支持块内的查询。对于大小为
现在我们可以在
优化询问复杂度
容易想到二分高度,然后可以
我们假设
- 每一块的大小都是
的整数幂次; - 每一层上的块大小是相同的。
为此我们需要在序列的末位补充一些
现在我们可以轻松地确定一个询问区间是否被整个地包含在一个块中。对于区间
我们知道每一层的区间长度是相同的,而分块的大小也是相同的(在上述示例中
因此我们需要检查区间两个端点是否只有后
- 对于每个
,我们找到找到 最高位上的 ; - 现在对于一个询问
,我们计算 的最高位,这样就可以快速确定答案区间所在的层。
这样我们就可以在
更新元素的过程
我们可以在 Sqrt Tree 上更新元素,单点修改和区间修改都是支持的。
单点修改
考虑一次单点赋值操作
朴素实现
首先我们来看看在做了一次单点修改后 Sqrt Tree 会变成什么样子。
考虑一个长度为
使用 Sqrt Tree 替代 B 数组
注意到单点更新的瓶颈在于更新根结点的
因此我们可以这样更新
- 在
的时间内更新 和 。 - 更新
,它的长度是 的,但我们只需要更新其中的一个元素(这个元素代表了被改变的块),这一步的时间复杂度是 的(使用朴素实现的算法)。 - 进入产生变化的子节点并使用朴素实现的算法在
的时间内更新信息。
注意,查询的复杂度仍是
更新一个区间
Sqrt Tree 也支持区间覆盖操作
我们可以像线段树一样在 Sqrt Tree 上打懒标记。但是在 Sqrt Tree 上有一点不同。因为下传一个结点的懒标记,复杂度可能达到
第一种实现
在第一种实现中,我们只会给第
-
考虑第
层上的结点,对于那些被修改区间完全包含的结点,给他们打一个懒标记; -
有两个块只有部分区间被覆盖,我们直接在
的时间内 重建 这两个块。如果它本身带有之前修改的懒标记,就在重建的时侯顺便下传标记; -
更新根结点的
和 ,时间复杂度 ; -
重建
树,时间复杂度 。
现在我们可以高效完成区间修改了。那么如何利用懒标记回答询问?操作如下:
-
如果我们的询问被包含在一个有懒标记的块内,可以利用懒标记计算答案;
-
如果我们的询问包含多个块,那么我们只需要关心最左边和最右边不完整块的答案。中间的块的答案可以在
树中查询(因为 树在每次修改完后会重建),复杂度是 。
因此询问的复杂度仍为
第二种实现
在这种实现中,每一个结点都可以被打上懒标记。因此在处理一个询问的时侯,我们需要考虑祖先中的懒标记,那么查询的复杂度将变成
- 被修改区间完全包含的块,我们把懒标记添加到这些块上,复杂度
; - 被修改区间部分覆盖的块,更新
和 ,复杂度 (因为只有两个被修改的块); - 更新
树,复杂度 (使用同样的更新算法); - 对于没有索引的子树更新他们的
; - 递归地更新两个没有被完全覆盖的区间。
时间复杂度是
实现
下面的实现在
习题
本页面主要译自 Sqrt Tree - Algorithms for Competitive Programming,版权协议为 CC-BY-SA 4.0。
贡献者:@Tifa@WenzelTian@Flex@mgt@Chrogeek@ouuan@sshwy@Margatroid
本页面最近更新:2/3/2023, 12:00:00 AM,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用