倍增分块与线段树底层分块

倍增分块与线段树底层分块

EnofTaiPeople

·

2023-02-15 21:55:51

·

题解

Part1 前言

这道题可以使用根号分块吗?我想了许久也没想出答案。

所以我打算学习一下倍增分块。

如果这题难点在于卡常的话,我倒不必急于去写这道题的代码和题解,然而这道题不仅不卡常,还具有十分有价值的倍增思想。

底层分块并不能算是在卡常,因为它确实优化了空间复杂度。

Part2 题意及前置知识

对区间大于 x 的数减去 x,区间求和。

前置知识:线段树,均摊复杂度分析。

### Part3 倍增分块的用法

倍增分块用处大多在于数值修改单调的情形,比如本题。

考虑将 $\lfloor\log_2a_i\rfloor$ 相同的 $a_i$ 放在同一个块内,每一个块开一棵维护区间 $[1,n]$ 的线段树,但只维护块内的数值和。

以下定义“变块”表示 $a_i$ 在修改时 $\log_2a_i$ 变小导致 $a_i$ 所在值域块产生变化。

考虑一次修改 $l,r,x$:

1. $\lfloor\log_2a_i\rfloor<\lfloor\log_2x\rfloor$,则 $a_i$ 所在块不会产生变化;

2. $\lfloor\log_2a_i\rfloor=\lfloor\log_2x\rfloor$,这时使用线段树最值剪枝找到块内所有 $a_i>x$,这时的 $a_i$ 一定会变块;

3. $\lfloor\log_2a_i\rfloor>\lfloor\log_2x\rfloor$,这时在区间 $[l,r]$ 的线段树上打懒标记,并线段树最值剪枝找到所有变块的数值。

倍增分块的优秀之处在于,每一个数字的变块次数最多为 $\log_2V$,总变块次数最多为 $n\log_2V$。

于是,对于第 $2$ 种,找到一个 $a_i>x$,复杂度为 $O(\log_2n)$,而这时会产生一次变块,所以是均摊 $O(n\log_2n\log_2V)$。

对于 第 $3$ 种,打标记复杂度为 $O(\log_2n)$,每找到一个需要变块的数字复杂度为 $O(\log_2n)$,这部分是均摊 $O(n\log_2n\log_2V)$。

查询就对每一个值域块分别查询就可以了。

### Part4 底层分块的意义

容易发现上面的空间复杂度是 $O(n\log_2V)$,而我们需要线性空间。

一个好方法是每一个块改为维护平衡树,这样可能时间卡常并且太难写所以我没去试,但用 AVL 应该可以卡过。

发现所有线段树建立的区间都是 $[1,n]$,而且最下面的几层最没有用却节点最多,所以可以将区间长度不再超过 $\log_2n$ 的节点不再新增叶子节点。

这样做为什么不影响时间复杂度?因为线段树区间查询的性质是每一次最多递归到 $O(1)$ 个叶子节点,所以时间不变。

### Part5 效率分析及改动

首先不一定要将 $2$ 作为倍增分块的参数,$14$ 是常数较小的,因为每个数字的变块次数和块数变少,大大减少了时空常数。

可以用 $40$ 作为底层分块的参数,这时可以稳过。

总时间复杂度 $O(n\log_2n\log_2V)$,空间 $O(n)$。