Preface
- 貌似n年前就听学长讲过,可惜当时太菜,什么都听不懂……
- 最近
太闲于是稍微学习了一下。在此做个小结。
一些约定/定义
- 点(i)的子树大小表示为(size(i))。
- 点(i)的势函数(R(i)=lceillog_2size(i) ceil)。注意这里有一个美妙的上取整。
- 全局势函数(Phi=sum_i R(i))。
- (Phi(t))表示时刻(t)的(Phi),即进行(t)次伸展操作后的(Phi)。显然对于任意(tin N),都有(Phi(t)in[n,nlog_2n])。
splay
- 只需分析伸展操作的复杂度。
分析
- 回顾一下rotate:若父亲是根,单旋一次;否则,若父亲和祖父方向一致,先旋父亲再旋自己;否则旋两次自己。因此我们只需分析zig、zig-zig、zig-zag。
- 博主太懒了,盗几张图吧……
- 注意,这里面写的(1+R'(x)-R(x))什么的实际上是全局势函数的变化量与新增耗时的总和,即(DeltaPhi+Delta T)。这大概是我以前没听懂的主要原因吧……
- 把zig-zag的代价也放缩成(≤3(R'(x)-R(x)))。那么如果我们把一个点(v)伸展到根,总代价(W≤1+3(R(root)-R(v))=O(log n))。换句话说,一次伸展操作会使全局势函数与耗时的总和变化(W),并且这个(W)是(O(log n))的。
- 那么如果我们对一棵大小为(n)的splay做(m)次伸展操作,其均摊复杂度(T=sum W-Phi(m)+Phi(0)=O((n+m)log n))。
- 这时我们就不得不佩服Daniel Sleator和Robert Endre Tarjan的脑子,居然能想到这种神奇的双旋方式,将(Delta T=1)美妙地混入(R'(x)-R(x))当中;而且我们可以发现,如果不这样旋,无法把(1)给合并进去。这也是胡乱旋会T飞的理由。
LCT
- 只需分析access操作的复杂度。
分析
- access的复杂度来源于两部分:1.在splay中走的复杂度;2.虚实变换的复杂度。
- 在splay中走的复杂度可以参考上文的证法,它的均摊复杂度也是(O((n+m)log n))的。((n)个点的LCT,进行(m)次access操作)
- 接下来考虑虚实变换。
- 参照重链剖分,我们定义从(u)连向满足(size(v)≥frac{size(u)}2)的(v)的虚边为重虚边,(size(v)<frac{size(u)}2)的为轻虚边。
- 定义势能函数(p)表示LCT中重虚边的数量。和链剖一样,每次最多走(log_2 n)条轻虚边,也就至多使(p+=log_2 n),这部分最坏情况下花(O(log n))的时间使(p)增加(log_2 n);而每访问一条重虚边,我们就要花(O(1))的时间使(p--),并且此时不会有新的重虚边产生。
- 因此虚实变换的均摊复杂度是(O(n+mlog n))。
- 综上,LCT的均摊复杂度为(T=O((n+m)log n))。
参考文献
怎么感觉“文献”这个词莫名喜感……- Splay和LCT的复杂度分析
- 均摊分析简介