動機
為什麼能倍增?
將2的次冪排成一個序列,即1,2,4,8,16,32……在這個序列中取任意個不相等的數,相加可以得到所有正整數
Sparse Table
定義st[i][j] => [i, i+2^j -1]
這是閉區間
之後把這個range二分,[i, i+2^(j-1)-1] [i+2^(j-1), i+2^(j-1)-1]
st[i][j] = st[i][j-1] + st[i+2^(j-1)][j-1]
from math import log2
def make_st(arr):
st = {}
K = log2(len(arr))
for i,n in enumerate(arr):
st[(i, 0)] = n
for j in range(1,K+1):
for i in filter(lambda i: i+(1 << j) <= len(arr), range(len(arr))):
st[(i,j)] = st[(i, j-1)] + st[(i+(1 << (j-1)), j-1)]
return [K, st]
只要是有結合律的函數(+, min, max)都可以用ST或是線段樹,之類可以作區間操作的DS
LCA
定義grand[i][j]
為節點i高2^j個level的祖先
那這要怎麼遞迴定義? 二分 所以用2^j-1去分,所以先跳到2^j-1,再往上跳2^j-1
grand[i][j] = grand[grand[i][j-1]][j-1]
from collections import defaultdict
from math import log2, ceil
def build(tree):
grand = defaultdict(lambda x: None)
dep = defaultdict(int)
dep[None] = -1
def dfs(root, parent=None):
if root:
grand[(root, 0)] = parent
dep[root] += dep[parent]+1
j = 1
while (1 << j) <= dep[root]:
grand[(root, j)] = grand[(grand[(root,j-1)], j-1)]
j += 1
dfs(root.left)
dfs(root.right)
dfs(tree)
return [dep, grand]
之後的查詢就是
- 先把兩個調到一樣高
- 從最大的2次方去試,直到兩邊相遇
dep, grand = build(tree)
def lca(a, b):
if dep[b] < dep[a]:
return lca(b, a)
else:
K = ceil(log2(len(dep.keys())))
for j in reversed(range(K+1)):
if dep[b]-(1 << j) >= dep[a]:
b = grand[(b, j)]
if a is b:
return a
else:
for j in reversed(range(K+1)):
if grand[(b, j)] is not grand[(a, j)]:
a, b = grand[(a, j)], grand[(b, j)]
return grand[(a, 0)]