sqrt tree 瞎写——以 RMQ 为例

感觉猫树白学了

本文纯个人理解 + 口胡,如果有什么问题请往死里喷我


我决定从期望 $O(1)$ RMQ 开始说,考虑先对序列分块,块长 $O(\sqrt{n})$ ,然后维护块内前后缀信息和每两个块之间的信息

但是期望 $O(1)$ RMQ 不是有个缺点?

同块的时候是不是会退化成 $O(\sqrt{n})$ ?

那万一有别有用心的人来卡你怎么办?

我们发现我们其实还是在做一段更小区间的 RMQ

因此然后考虑我们 RMQ 是怎么做的

我决定从期望 $O(1)$ RMQ 开始说,考虑先对序列分块,块长 $O(\sqrt{\sqrt{n} })$ ,然后维护块内前后缀信息和每两个块之间的信息

但是期望 $O(1)$ RMQ 不是有个缺点?

同块的时候是不是会退化成 $O(\sqrt{\sqrt{n} })$ ?

那万一有别有用心的人来卡你怎么办?

我们发现我们其实还是在做一段更小区间的 RMQ

因此然后考虑我们 RMQ 是怎么做的

……

然后当序列长度足够小没必要分块的时候,就可以 $O(1)$ 查询的

于是我们得到了一个树形结构,这就是 sqrt tree


首先发现这东西层数 $O(\log \log n)$

因此明确其建树是 $O(n\log \log n)$ 的

考虑这里如果直接查询要扫 $O(\log \log n)$ 层直到一个可以 $O(1)$ 的区间

虽然还是期望 $O(1)$ 的((

但是确实可以被卡到 $O(\log \log n)$ 不是吗((

想想隔壁猫树是不是也遇到过类似的问题?

可以类似解决吗?

我们照样给它整成 $2^k$ 的形式然后试试能不能二进制

说人话就是所有块长都是 $2^k$ 的形式

那么显然块数也是 $2^k$ 的形式了

不难发现可以用位运算优化这个过程了,具体实现和猫树差不多,留给读者自行思考

不过我个人感觉这里倍增啊二分啊什么的都已经是 $O(\log \log \log n)$ 的了,基本上可以忽略不计了,至于吗(恼


考虑修改

隔壁猫树修改比直接重构优秀,看看 sqrt tree 行不行

朴素修改的话显然我们每一层都得重构一个结点,是 $O(n+\sqrt{n}+\sqrt{\sqrt{n}}+\dots)=O(n)$ 的

这不行啊,这怎么能给我们带来笑容呢

此时我们惊讶的发现,我们在原序列上的整块上做的居然也是 RMQ

于是我们再对于整块开一个 sqrt tree ,而不开什么块间的数组了

这样一来复杂度就是 $O(\sqrt{n}+\sqrt{\sqrt{n}}+\dots)=O(\sqrt{n})$ 单次修改了


区间修改有两种实现方法

第一种实现是考虑我们区间修改每一层是两个散块,那么就对这 $O(1)$ 个散块暴力重构,复杂度就会多一个 $O(\log \log n)$

第二种实现是给被完整修改的结点全部打上标记,那么修改复杂度不变,查询复杂度由于要查询祖先,就多了一个 $O(\log \log n)$


看了一下 OI-wiki 上的代码实现,感觉挺阴间的

口胡了一下可能可以用 ¥gg 论文里面 vEB 树的那种嵌套写法,可能会比 OI-wiki 上的好一些

感觉实战中肯定没什么用,正常人谁闲得无聊写这玩意儿啊