lca and rmq ~簡潔もあるよ!~
TRANSCRIPT
ERATO若手輪読会 2014/11/19
LCA and RMQ北海道大学大学院 情報科学研究科
博士1年 井上 祐馬
1
ERATO若手輪読会 2014/11/19
• LCA: Lowest Common Ancestor (最近共通祖先) • 根付き木 T 上の2頂点 u, v に対するクエリ LCA(u,v) • u と v の祖先であって、もっとも深い頂点 x を返す
• RMQ: Range Minimum Query (区間最小値) • 列 A[1:n] 上の区間 [l, r] に対するクエリ RMQ(l,r) • A[l:r] 中での最小値 A[i] を取るような i を返す
• LCA と RMQ には密接な関係がある
LCAとRMQ
2
u
v
x
id 1 2 3 4 5 6A[id] 1 8 2 6 3 5
l r
i
T
ERATO若手輪読会 2014/11/19
LCAとRMQ• クエリ処理アルゴリズムの計算量表記: <f(n), g(n)> • f(n): 前処理時間, g(n): 1つのクエリの処理時間
3
・LCA と RMQ を <O(n), O(1)> で解く ・(時間があれば)空間 2n + o(n) bit の 簡潔データ構造で <O(n), O(1)> で処理する
今日の目標
ERATO若手輪読会 2014/11/19
LCAとRMQ• クエリ処理アルゴリズムの計算量表記: <f(n), g(n)> • f(n): 前処理時間, g(n): 1つのクエリの処理時間
4
LCA を <f(n), g(n)> で解くアルゴリズムが存在 → RMQ は <f(n)+O(n), g(n)+O(1)> で解ける
RMQ を <f(n), g(n)> で解くアルゴリズムが存在 → LCA は <O(f(n))+O(n), O(g(n))+O(1)> で解ける
定理1
定理2
ERATO若手輪読会 2014/11/19
RMQ to LCA
5
LCA を <f(n), g(n)> で解くアルゴリズムが存在 → RMQは <f(n)+O(n), g(n)+O(1)> で解ける
定理1
• 列Aを Cartesian Tree に変換 • Cartesian Tree • 値の2分ヒープ構造 • 頂点ラベルはインデックス • 子の左右が列の左右と一致 id 1 2 3 4 5 6
A[id] 1 8 2 6 3 5
13
2
5
46
ERATO若手輪読会 2014/11/19
RMQ to LCA
6
• RMQ(l,r):Cartesian Tree上でLCA(l,r)を解くことで最小値インデックスが求まる
• Cartesian Treeの構築: O(n) • 最小値そのものが知りたいとき:メモリアクセス O(1)
id 1 2 3 4 5 6A[id] 1 8 2 6 3 5
13
2
5
46
LCA を <f(n), g(n)> で解くアルゴリズムが存在 → RMQは <f(n)+O(n), g(n)+O(1)> で解ける
定理1
ERATO若手輪読会 2014/11/19
LCA to RMQ
• 木 T を Euler Tour で展開 • Euler Tour: • TをDFSで辿る • 頂点の訪問順で列を定義 • 列の長さは必ず2n-1
7
RMQ を <f(n), g(n)> で解くアルゴリズムが存在 → LCA は <O(f(n))+O(n), O(g(n))+O(1)> で解ける
定理2
4 65
2
1
3
T
id 1 2 3 4 5 6 7 8 9 10 11
A[id] 1 2 1 3 4 3 5 3 6 3 1
ERATO若手輪読会 2014/11/19
LCA to RMQ
• LCA(u,v): 深さ配列d上でRMQ(l,r)(l, rはu, vのAでの出現位置)
• Euler Tourへの展開: O(n) • LCAの頂点番号取得: メモリアクセス O(1)
8
RMQ を <f(n), g(n)> で解くアルゴリズムが存在 → LCA は <O(f(n))+O(n), O(g(n))+O(1)> で解ける
定理2
4 65
2
1
3
T
id 1 2 3 4 5 6 7 8 9 10 11
A[id] 1 2 1 3 4 3 5 3 6 3 1
d[id] 0 1 0 1 2 1 2 1 2 1 0
ERATO若手輪読会 2014/11/19
LCAとRMQ
• 定理より、LCA または RMQ のどちらか一方が <O(n), O(1)> で解ければ、もう一方も<O(n), O(1)>
• LCAとRMQには、それぞれ様々なアルゴリズムが提案されている
9
ERATO若手輪読会 2014/11/19
RMQのアルゴリズム
10
空間 前処理 クエリ
Segment Tree (動的) O(n) O(n) O(log n)
Sparse Table O(n logn) O(n logn) O(1)
Cartesian Tree + LCA O(n) + ? O(n) + ? O(1) + ?
※ここでは各値を表すのに必要な O(logn) は無視
ERATO若手輪読会 2014/11/19
LCAのアルゴリズム
11
空間 前処理 クエリ
Doubling O(n logn) O(n logn) O(log n)
Heavy Path Decomposition O(n) O(n) O(1)
Euler Tour + RMQ O(n) + ? O(n) + ? O(1) + ?
※ここでは各値を表すのに必要な O(logn) は無視
ERATO若手輪読会 2014/11/19
<O(n), O(1)>アルゴリズム
• LCAはHeavy Path Decompositionを利用して、直接 <O(n), O(1)> で解くことが可能
• ただし、簡潔化が難しい
• LCAで必要とされるRMQがある特殊な条件を満たすことを利用して、RMQを <O(n), O(1)> で解く
12
ERATO若手輪読会 2014/11/19
±1RMQ• LCA: Euler Tour の深さ配列でRMQ • 1回の移動で深さはちょうど1だけ変わる • 深さ配列の隣接要素同士は+1 か -1 だけ異なる
• 隣接要素の変化が ±1 である列上での RMQ を ±1RMQ と呼ぶ • ±1RMQ が <O(n), O(1)> で解ければ、LCA も <O(n), O(1)> で解ける
134 65
2
1
3
id 1 2 3 4 5 6 7 8 9 10 11
A[id] 1 2 1 3 4 3 5 3 6 3 1
d[id] 0 1 0 1 2 1 2 1 2 1 0
+1 +1 +1 +1 +1 -1 -1-1-1-1
ERATO若手輪読会 2014/11/19
<O(n), O(1)> RMQRMQを <O(n), O(1)> で解く流れ 1. RMQ を Cartesian Tree に変換し、LCA に帰着( 時間・空間: O(n) )
2. LCA を Euler Tour で展開し、±1RMQ に帰着( 時間・空間: O(n) )
3. ±1RMQ を以下のテクニックで <O(n), O(1)> で解く • ブロック分割 • Sparse Table • Table Lookup
14
[Bender et. al. '05]
ERATO若手輪読会 2014/11/19
ブロック分割• 簡潔データ構造の典型テクニック • 長さnの列を長さs = logn/2のブロックに分割 • ブロックの数 B = 2n/logn • 各ブロックの最小値インデックスを O(n) で前計算
• 全体の RMQ クエリは、以下のクエリに分割される • ブロックの区間に対する RMQ クエリ1回 • ブロック内部に対する RMQ クエリ2回
15
id 1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930A[id] 1 2 1 3 4 3 5 3 6 3 1 8 4 9 11 4 1 5 6 3 2 5 2 5 2 1 9 8 4 1
ERATO若手輪読会 2014/11/19
ブロック分割• 簡潔データ構造の典型テクニック • 長さnの列を長さs = logn/2のブロックに分割 • ブロックの数 B = 2n/logn • 各ブロックの最小値インデックスを O(n) で前計算
• 全体の RMQ クエリは、以下のクエリに分割される • ブロックの区間に対する RMQ = Sparse Table • ブロック内部に対する RMQ = Table Lookup
16
id 1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930A[id] 1 2 1 3 4 3 5 3 6 3 1 8 4 9 11 4 1 5 6 3 2 5 2 5 2 1 9 8 4 1
それぞれ <O(n), O(1)> で 解ければOK
ERATO若手輪読会 2014/11/19
Sparse Table• i 番目から長さ2kの区間の最小値インデックスをそれぞれ記憶したO(n logn)のテーブルを持つ
• 構築: O(n logn) • S[i][k+1] = argmin( A[S[i][k]], A[S[i+2k][k]] )
• クエリ: O(1) • RMQ(i, j) = argmin( A[S[i][k]], A[S[j-2k+1][k]] ) • k = floor( log(j-i) ) ← O(1)で求められると仮定
17
id 1 2 3 4 5 6 7 8A[id] 1 2 1 3 4 3 5 3
id 1 2 3 4 5 6 7 8A[id] 1 2 1 3 4 3 5 3構築: クエリ:
ERATO若手輪読会 2014/11/19
Sparse Table• i 番目から長さ2kの区間の最小値インデックスをそれぞれ記憶したO(n logn)のテーブルを持つ
• B個のブロックに対し、<O(B logB), O(1)> のアルゴリズム
• B = 2n/lognより、B logB = 2n/logn・(log2n - loglogn) = O(n)
18
id 1 2 3 4 5 6 7 8A[id] 1 2 1 3 4 3 5 3
id 1 2 3 4 5 6 7 8A[id] 1 2 1 3 4 3 5 3構築: クエリ:
ERATO若手輪読会 2014/11/19
Table Lookup• 簡潔データ構造の典型テクニック その2 • ±1 で変化している列の最小値インデックスは、値そのものを見なくても変化量だけ見ればわかる
• すべての変化パターンと区間 [l,r]について、最小値インデックスを記録して参照すればよい • 長さ s の列の変化のパターンは2s通り • よって、<O(s22s), O(1)> のアルゴリズム • s = logn/2より、s22s = O(log2n √n) = o(n)
19
ERATO若手輪読会 2014/11/19
<O(n), O(1)> RMQRMQ を <O(n), O(1)> で解けた!!! 1. RMQ を Cartesian Tree に変換し、LCA に帰着( 時間・空間: O(n) )
2. LCA を Euler Tour で展開し、±1RMQ に帰着( 時間・空間: O(n) )
3. ±1RMQ を以下のテクニックで <O(n), O(1)> で解く • ブロック分割 • Sparse Table • Table Lookup
20
[Bender et. al. '05]
ERATO若手輪読会 2014/11/19
-----↓ここから簡潔↓-----
21
ERATO若手輪読会 2014/11/19
簡潔RMQRMQ を <O(n), O(1)> で解けた!!! 1. RMQ を Cartesian Tree に変換し、LCA に帰着( 時間・空間: O(n) )
2. LCA を Euler Tour で展開し、±1RMQ に帰着( 時間・空間: O(n) )
3. ±1RMQ を以下のテクニックで <O(n), O(1)> で解く • ブロック分割 • Sparse Table • Table Lookup
22
BP (Balanced Parentheses) 表現で2n bitに
BP上の rank( - rank) が深さに対応 → Euler Tour なしに ±1RMQ
分割を多段にすることで o(n) bitに
[Sadakane '07]
ERATO若手輪読会 2014/11/19
簡潔RMQ• 問題点: BP 上でわかるのは pre-order ⇔ RMQ で求めたいインデックスは Cartesian tree 上では in-order • 対応を覚える場合O(n logn) bit を使う → not 簡潔
• Cartesian Tree に dummy node を n 個付加することで解決 → 4n + o(n) bitに
23
[Sadakane '07]
id 1 2 3 4 5 6A[id] 1 8 2 6 3 5
13
2
5
46( ( ( ) ( ( ) ( ) ) )
1 13 22 8
4 65 3
6 5
ERATO若手輪読会 2014/11/19
簡潔RMQ• 問題点: BP 上でわかるのは pre-order ⇔ RMQ で求めたいインデックスは Cartesian tree 上では in-order • 対応を覚える場合O(n logn) bit を使う → not 簡潔
• Cartesian Tree に dummy node を n 個付加することで解決 → 4n + o(n) bitに
24
[Sadakane '07]
id 1 2 3 4 5 6A[id] 1 8 2 6 3 5
13
2
5
46( ( ( ) ( ( ) ( ) ) )
1 13 22 8
4 65 3
6 5
2nにしたい
ERATO若手輪読会 2014/11/19
簡潔RMQ• Cartesian Tree + BP の代わりに 2D-Min Heap + DFUDS (Depth-First Unary Degree Sequence)
• 流れ: • 入力列を 2D-Min Heap を表すDFUDSに変換 • O(n)時間、 DFUDSは 2n bit
• DFUDS に必要な索引を付加 • O(n)時間、索引は o(n) bit
• 索引付き DFUDS 上で LCAを解く • 内部で rank, select, findopen, ±1RMQ 等を使用 • O(1) 時間
25
[Fischer '09]
id 0 1 2 3 4 5 6A[id] -∞ 1 8 2 6 3 5
1
32
54
6
2D-Min Heap
0
DFUDS: ( ( ) ( ( ) ) ( ( ) ) ( ) )
ERATO若手輪読会 2014/11/19
簡潔RMQ• Cartesian Tree + BP の代わりに 2D-Min Heap + DFUDS (Depth-First Unary Degree Sequence)
• 2D-Min Heap: • 自分より左かつ小さいもののうち、最も右にあるものを親にする
26
[Fischer '09]
id 0 1 2 3 4 5 6A[id]-∞ 1 8 2 6 3 5
13
2 54 6
13254
6Cartesian Tree 2D-Min Heap
0
ERATO若手輪読会 2014/11/19
簡潔RMQ• RMQ(i, j)は、2D-Min Heap 上では、 • LCA(i, j)が i → RMQ(i, j) = i • それ以外 → RMQ(i, j) = LCA(i, j)の子で、jの先祖
27
[Fischer '09]
id 0 1 2 3 4 5 6A[id]-∞ 1 8 2 6 3 5
2D-Min Heap
13254
6
0i j LCA(2,4)
RMQ(2,4)
ERATO若手輪読会 2014/11/19
簡潔RMQ• Cartesian Tree + BP の代わりに 2D-Min Heap + DFUDS (Depth-First Unary Degree Sequence)
• DFUDS: • 葉は ( ) • w 個の子 T1, ..., Tw の親は w+1 個の ( と 1個の ) の後、子を続ける.ただし、子の表現は先頭の ( を1つ削る
28
[Fischer '09]
( ( ) ( ( ) ) ( ( ) ) ( ) )id 0 1 2 3 4 5 6d 1 2 1 2 3 2 3 4 3 4 3 2 1 0
A[id] -∞ 1 8 2 6 3 5
13254
6
0
ERATO若手輪読会 2014/11/19
• RMQ(i, j) 1. l ← select)(U, i+1), r ← select)(U, j) 2. w = ±1RMQ(l, r) 3. if rank)(U, findopen(U, w)) = i, then return i 4. else rank)(U, w)
29
簡潔RMQ [Fischer '09]
U: ( ( ) ( ( ) ) ( ( ) ) ( ) )
id 0 1 2 3 4 5 6d 1 2 1 2 3 2 1 2 3 2 1 2 1 0
A[id] -∞ 1 8 2 6 3 5
13254
6
0 i j
l r
±1RMQ(l,r)
findopen
ERATO若手輪読会 2014/11/19
• RMQ(i, j) 1. l ← select)(U, i+1), r ← select)(U, j) 2. w = ±1RMQ(l, r) 3. if rank)(U, findopen(U, w)) = i, then return i 4. else rank)(U, w)
30
簡潔RMQ [Fischer '09]
U: ( ( ) ( ( ) ) ( ( ) ) ( ) )
id 0 1 2 3 4 5 6d 1 2 1 2 3 2 1 2 3 2 1 2 1 0
A[id] -∞ 1 8 2 6 3 5
13254
6
0 i j
l r
±1RMQ(l,r)
findopen
DFUDS 上の RMQ では、 w は i と j のLCAの子であり、
jの祖先である (ただし LCA が iのとき w=i ) [Jansson et. al. '07]
ERATO若手輪読会 2014/11/19
• RMQ(i, j) 1. l ← select)(U, i+1), r ← select)(U, j) 2. w = ±1RMQ(l, r) 3. if rank)(U, findopen(U, w)) = i, then return i 4. else rank)(U, w)
31
簡潔RMQ [Fischer '09]
U: ( ( ) ( ( ) ) ( ( ) ) ( ) )
id 0 1 2 3 4 5 6d 1 2 1 2 3 2 1 2 3 2 1 2 1 0
A[id] -∞ 1 8 2 6 3 5
13254
6
0 i j
l r
±1RMQ(l,r)
findopen
親が preorder わかる
ERATO若手輪読会 2014/11/19
• RMQ(i, j) 1. l ← select)(U, i+1), r ← select)(U, j) 2. w = ±1RMQ(l, r) 3. if rank)(U, findopen(U, w)) = i, then return i 4. else rank)(U, w)
32
簡潔RMQ [Fischer '09]
U: ( ( ) ( ( ) ) ( ( ) ) ( ) )
id 0 1 2 3 4 5 6d 1 2 1 2 3 2 1 2 3 2 1 2 1 0
A[id] -∞ 1 8 2 6 3 5
13254
6
0 i j
l r
±1RMQ(l,r)
findopen自分の preorder わかる
ERATO若手輪読会 2014/11/19
割愛したところ• 入力列から直接 2n bit のDFUDSへ O(n) で変換 • 簡潔でない方法: スタックを利用したアルゴリズム • 簡潔な方法: スタックを簡潔にする
• ±1RMQ をより簡潔に • o(n) で隠された部分がより succinct に • 具体的には、(loglogn)2 かかっていたところが loglogn に落とせる
33
ERATO若手輪読会 2014/11/19
まとめ• LCA と RMQは適切に変換することで等価な問題として解ける • LCA to RMQ: Euler Tour, BP, DFUDS • RMQ to LCA: Cartesian Tree, 2D-Min Heap
• LCA で要求される RMQ が ±1RMQ であることを利用し、<O(n), O(1)> を実現する
• 木の簡潔表現と LCA, RMQ の簡潔辞書により、2n + o(n) bitで、<O(n), O(1)> を実現する
34