原文鏈接www.cnblogs.com/zhouzhendong/p/JunTanFenXi.html
本文概要
1. 引入
2. 簡單例子
3. 證明 splay 復雜度
4. 證明 LCT 復雜度
引入
為什么 KMP 不能可持久化,而要用 KMP 自動機來代替?
為什么 splay 不能可持久化,僅僅只是因為難以維護 father 指針嗎?
答案是——它們都是基於均攤分析的。
均攤時間復雜度是什么?
這里有一個容易混淆的概念:期望時間復雜度。
期望時間復雜度是指在隨機情況下,算法的每種時間復雜度乘上它發生的概率之和。
而均攤時間復雜度不一樣。它是指總復雜度在一個范圍內,但是單次操作的復雜度並不是總復雜度除以操作數,甚至有可能接近總復雜度。
本文主要部分參照陳胤伯的《均攤分析簡介》改寫。
簡單例子
問題
一個初始值為 0 的 k 位二進制計數器,每次加一。每次加一的運算次數為修改的位數。請問時間復雜度是什么?
證明與答案
作為一個簡單例子,自然有很多簡單證法。
例如,這個 k 位二進制數第 i 位,需要每加 $2^i$ 次才會變一次。所以所有位的變化次數之和為
$$\sum_i \frac{n}{2^i} = O(n)$$
所以單詞操作均攤 $O(1)$ 。
接下來,引入一種更通用的方法——勢能分析。
定義勢能函數 $\phi$ 表示定義域當前狀態的一個函數。
定義 $\phi(i)$ 為第 i 次操作后的狀態的勢能函數值。
定義第 i 次操作的實際消耗時間為 $t_i$ 。
定義第 i 次操作的均攤時間話費為 $a_i$ ,$a_i = t_i + \phi(i) - \phi(i-1) $ 。
為了方便書寫,如不加說明,則用 $\Delta phi$ 表示 $\phi(i)-\phi(i-1)$ 。
於是我們可以用上述定義得到總時間復雜度為
$$\phi(0)-\phi(n)+\sum_{i=1}^{n} a_i$$
接下來回到原先的例子:
定義 $\phi$ 表示當前 k 位二進制數中值為 1 的位的個數,顯然第 i 次操作有一個 0 變成 1,設第 i 次操作有 x 個 1 變成 0 。那么
$$a_i=t_i+\Delta\phi = (1+x) + (1-x) = 2$$
所以總時間復雜度為
$$\phi(n) - \phi(0) + \sum_{i=1}^n a_i \leq k + 2n = O(k+n)$$
朴素地講,我們可以把勢能函數看做自己的存款,在耗時的時候消耗以換取時間,在不耗時的時候存儲。只要總的變化量在一定范圍內,那么總的復雜度就可以被接受。
證明 Splay 的復雜度
相信經過一定的思考,你會發現勢能分析的一個關鍵在於確定合適的勢能函數 $\phi$ 。接下來,在證明 splay 復雜度的過程中,你將會體驗到勢能分析的另一個關鍵——合理放縮。
定義
設 splay 樹的大小和操作次數都是 $O(n)$ 。
定義 $size[x]$ 表示節點 x 的子樹大小。
定義函數 $f(x) = \log_2 (size[x])$,接下來簡寫為 $f(x) = \log (size[x])$ 。注意這個底數 2 是不能忽視的。
定義 $f'(x)$ 表示本次操作之后的 $f(x)$ 。
定義 $\phi = \sum f(x)$ 。
splay 的操作可以分為單旋和雙旋,這里定義單次單旋或者雙旋的均攤時間花費為 $S(x)$ 。
證明
我們來給旋轉操作分成 3 類:
1. 單旋;
2. 雙旋 - 先旋 father ,再旋 x;
3. 雙旋 - 旋轉 x 兩次 。
我們來依次證明三種旋轉操作的復雜度。下面默認對節點 x 做旋轉操作。
單旋

$$\begin{eqnarray*}S(x) &=& 1 + f'(x) + f'(y) - f(x) - f(y) \\ &=& 1 + f'(y) - f(x) \\ &\leq & 1 + (f'(x) - f(x))\end{eqnarray*}$$
雙旋 - 先旋 father ,再旋 x

$$\begin{eqnarray*}S(x) &=& 2 + f'(x) + f'(y) + f'(z) - f(x) - f(y) - f(z) \\ &=& 2 + f'(y) + f'(z) - f(y) - f(x) \\ &\leq & 2 + f'(x) + f'(z) - 2f(x) \end{eqnarray*}$$
由於
$$\begin{eqnarray*}f(x) + f'(z) - 2f(x) &=& \log_2\left(\frac{size[x] \cdot size'[z]}{size'[x] ^2 }\right)\end{eqnarray*}$$
又因為
$$size[x] + size'[z] = size[1] + size[2] + size[3] + size[4] + 2 \leq size'[x] $$
所以
$$size[x] \cdot size'[z] \leq \frac 1 4 size'[x] ^ 2$$
所以
$$f(x) + f'(z) - 2f(x) \leq \log_2 (\frac 1 4) = -2 $$
即
$$-2 - (f(x) + f'(z) - 2f(x))\geq 0{\tag 1}$$
將原式加上 (1) 式,得到
$$S(x) \leq 2 + f'(x) + f'(z) - 2f(x) + (-2 - (f(x) + f'(z) - 2f(x))) = 3(f'(x) - f(x))$$
雙旋 - 旋轉 x 兩次

$$\begin{eqnarray*}S(x) &=& 2 + f'(x) + f'(y) + f'(z) - f(x) - f(y) - f(z) \\ &=& 2 + f'(y) + f'(z) - f(y) - f(x) \\ &\leq & 2 + f'(y) + f'(z) - 2f(x) \end{eqnarray*}$$
類似於前一半,我們有
$$-2-(f'(y) + f'(z)-2f'(x))\geq 0$$
於是
$$S(x) \leq 2 + f'(y) + f'(z) - 2f(x) + (-2-(f'(y) + f'(z) - 2f'(x))) = 2(f'(x) - f(x))$$
綜上所述,我們可以將三種旋轉的單次均攤復雜度分別是
$$1 + (f'(x) -f(x))\\ 3(f'(x) - f(x)) \\ 2(f'(x) - f(x))$$
一次 splay 操作只會做一次 單旋,所以單旋復雜度里的那個多出來的 1 我們可以提出,對最終復雜度貢獻 $O(n)$ 。
於是我們把三種操作都放縮到 $3(f'(x) -f(x))$ 。
於是,如果設這次 splay 的起始點為 x,終止點為 y ,那么一次 splay 的均攤復雜度就是 $f(y) - f(x) = O(\log n)$ 。
由於 $\phi(0)$ 和 $\phi(n)$ 都是 $0$~$n\log n$ 范圍內的,所以總復雜度為
$$T(n) = \sum_{i=1}^n a_i + \phi(0) - \phi(n) \leq O(n\log n)$$
P.S. 對splay做切割與合並操作時,只要先把連接點旋到根就容易發現勢能函數發生變化的節點數是 $O(1)$ 的。
證明 LCT 的復雜度
定義
LCT 的證明和 Splay 類似。這里我們將 $size[x]$ 的定義稍加修改,改成輔助樹上 子樹 x 的大小。
證明
LCT中splay部分的證明和 Splay 的復雜度證明一樣。
這里只考慮虛實鏈切換時的復雜度。
如果 size[x]*2 >= size[fa[x]] ,那么我們稱 x 為 fa[x] 的重兒子,fa[x] 的其他兒子為輕兒子。
那么,顯然一個節點在 access 的過程中只會遇見 $O(\log n)$ 個輕兒子。
定義勢能函數 $\delta$ 表示實兒子和重兒子不同的節點個數。
考慮一次 access 。
考慮一次 splay 后,當前splay根節點 x 的情況:
1. 如果它是輕兒子,那么消耗 1 時間,勢能函數增量小於等於 1 。
2. 如果它是重兒子,那么消耗 1 時間,勢能函數減1 。
所以一次access 在輕重鏈切換上消耗的均攤時間就等於 1. 操作的均攤時間花費,所以一次access的均攤時間復雜度為 $O(\log n)$ 。
所以 $LCT$ 的時間復雜度是
$$O(n\log n)$$
換根、link、cut 的復雜度分析
我們需要證明做這些操作時輕重邊變化量合法。
換根
考慮將一個連通塊的根從 $x$ 變更為 $y$ 時,哪些邊的輕重發生了改變。我們發現,可能改變輕重的邊只有 $x$ 到 $y$ 路徑上的邊,而改變前和改變后這條路徑上均只有 $O(\log n)$ 個輕邊,所以變化量為 $O(\log n)$。
Link-Cut
考慮 Cut 操作:假設在以 $y$ 為根的連通塊上切除子樹 $x$,我們發現影響到的也只有 $x$ 到 $y$ 路徑上的邊,同理變化量還是 $O(\log n)$。
考慮 Link 操作:假設將根為 $x$ 的連通塊和根為 $y$ 的連通塊通過某兩個點相連,我們發現,如果我們取 $x$ 或者 $y$ 為新連通塊的根,那么影響到的仍然是連邊后 $x$ 到 $y$ 路徑上的邊,同理變化量還是 $O(\log n)$。
