斜二倍增 #超清大图
老年人来感受 2025 年的科技浪潮了!
"斜二倍增"想要说什么?
我认为整个算法始于这个观察:
当我们有一个支持 push_back 和区间查询的向量时,线段树给我们
但有趣的是:如果我们把这个向量看作树路径,那么 push_back 变成了添加叶子。突然间,传统的二进制提升("树上倍增")膨胀到
更仔细地看二进制提升结构,我认为它基本上只是应用于数组的"稀疏表"。这就是为什么一些论文称之为 "st-tree",据我所知。 真正的问题是:我们如何将那种优雅的线段树带到树上?
*
/ \
* * ^
/ \ |
....*.......*.... |
/ \ |
* * <-- u v
/ \
* *
关键洞察是:看图。我们在其中点(虚线)处切割树。对于节点
从线段树得到启发
要实现这个想法,我们先来看看线段树中每个右端点对应的区间长度:
8
4 4
2 2 2 2 2 2
111111111111
-------------------
index 123456789012
lowbit 010201030102
规律很明显:对于右端点
那么在树上呢,对于一个深度为
但是等一下...
看起来我们搞定了?
考虑这棵树:
*
|
*
.
.
*
/ | \ ....
* * * .... (最下面有很多叶子)
你看,最下面突然可以有很多叶子节点,每个叶子都要存
一个巧妙的想法 - 斜二进制
这引导我去考虑一个"斜着"的线段树结构:
(------------------------------)
(-------------)(--------------)
(-----)(-----) (-----)(-----)
(-)(-) (-)(-) (-)(-) (-)(-)
** ** ** ** ** ** ** **
--------------------------------------
1111111111222222222233
index 1234567890123456789012345678901
这里的美妙之处是:对于每个右端点
现在还不清楚如何高效地决定
如何建树:下面我们将使用"节点"而不是"右端点"。对于节点
如图所示:
u
|
v
(-----)
(-)(-)
^ ^
| |
w v
从这个更新,我们也知道如何确定
- 如果前两个区间长度
L(v) 和L(w) 相等,L(u) = 2 \, L(w) + 1 - 否则,它应该是
1
如何查询:要查询
- 如果
x \geq L(r) ,我们取(r - L(r), r] 并跳到r - L(r) - 否则,我们取元素,并继续
(r - (x - 1), r - 1]
至此,我们终于得到了一个
附注:我之前有过一个非常类似的困惑 - LCT 在