Decomposable searching problem - オフラインの場合
Latest Author uwi /Date 2016-09-07 17:33:45 / Views 6658この記事では前の記事で紹介した分解可能問にオフラインで答える場合に使えるテクニックを紹介する。 「オンライン」「オフライン」の違いや、オンラインの場合のテクニックについては前の記事を参照のこと。
ただのセグメント木
この記事で紹介することは「ただのセグメント木」である。 また、前の記事で紹介したテクニックもセグメント木を使っていると言える。セグメント木さえ知っていれば他に知識は必要ない。 以下に2種類の方法を説明する。
クエリを区間として見る場合
元となる要素を追加された順に並べて考えると、クエリはその列に対する区間クエリである。 セグメント木の各ノードは、そのノード区間が含む要素に対し静的データ構造を構築する。 クエリはそのセグメント木に対して、そのクエリ区間の分割である区間のノードそれぞれの静的データ構造に対しクエリをしたものを組み合わせればよい。
要素の追加のみの場合、$t$個の要素が追加されたすぐ後に答えるクエリは$[1,t]$の区間に対応する。 もちろん、その他の任意の時間区間に対してのクエリ("time-windowed query")を行うことができる。
そのまま実装すると各データ構造が線形空間の場合でも$Θ(n \log n)$空間かかるが、セグメント木を深さ優先探索しながら必要のある部分だけ構築するなどすれば線形空間にすることができる。
前記事で紹介して「マージ」による高速化やフラクショナルカスケーディングも使える。 ただ、この場合"フラクショナル"カスケーディングは必要なく、"フルカスケーディング"と言えるもの(一般的な言葉ではない)でできる。
要素を区間として見る場合
クエリを時間順に並べて考え、要素に対し「その要素を対象にするクエリ」という時間区間を割り当てる。 各要素の時間区間の分割に対応するセグメント木の各ノードに対しその要素を追加し、その追加された要素に対し静的データ構造を構築する。 クエリは、その時刻を含む各ノードに対し静的データ構造に対してクエリをしたものを組み合わせればよい。
要素の追加のみの場合、時刻$t$のクエリの直前に追加された要素は$[t,\infty)$の区間に対応する。最後のクエリが時刻$T$の場合は$[t,T]$とできる。
前記事で紹介した方法はこの「要素を区間として見る場合」である。ただし、最後のクエリの時刻というのはわからないため$[t,\infty)$に対応するが、 もちろん無限のノードは用意できないが、適切に考えると有限にできる。
この方法も適切にやれば空間計算量を削減できる。 フラクショナルカスケーディングも適用できるだろう。 また、一般には単純に「マージ」することはできないが、ソートをしたい場合には最初に全ての要素をソートしてその順に処理すれば各ノード内でソートする必要がなくなる。
要素の削除
この方法は要素の削除に対応する。 時刻$t$のクエリの直前に追加され、時刻$u$のクエリの直後に削除された要素は$[t,u]$の区間に対応する。 この要素の削除に関して演算子の可逆性などは一切仮定していないことに注意しよう。これはオフラインの場合の優位な点である。
さらにできること
部分的なオンライン性
上記の2つの方法はどちらも要素の内容とクエリのタイミングがオフラインで与えられることは仮定しているが、クエリの内容は使っていない。 よって、全ての要素とその追加・削除のタイミングさえ最初に与えられれば、クエリの内容自体はオンラインで与えられても答えることができる。
クエリもオフラインの場合
逆に、クエリの内容も全てオフラインで与えられる場合、できることが増えることがある。 今まで「静的データ構造」というものを仮定していたが、この場合はそれすら必要なく、「データとクエリ列が与えられたとき、全てのクエリに対しての答えを計算する」アルゴリズムさえあればよい。
たとえば、クエリがデータ上で二分探索をするという場合、最初にクエリもソートしておけばデータとクエリを「マージ」するだけで二分探索結果が得られる。 クエリのソートもデータのソートと同じくノードごとにやる必要はない。 つまり、フラクショナルカスケーディングなどのテクニックは必要なくなる。
分解可能とは言えない場合でさえ - Batched Dynamic Connectivity
以下の問題を考える "dynamic connectivity":
- 無向グラフを管理する。最初は辺が無い。
- 辺の追加・削除の操作が与えられる。
- 2つの頂点$u, v$が与えられ、「$u$と$v$は同じ連結成分に属するか?」というクエリに答える。
辺を要素とする。上で書いたように、要素を区間として見ればセグメント木のノードそれぞれに辺集合が割り当てられる。 トリックは、このセグメント木上をDFSすることである。
DFS中にUndo可能なUnion-findデータ構造を管理する。この実装に関してはここでは書かないが簡単に実装できる。 DFSにおいてセグメント木あるノードに到達した("in")とき、そのノードに割り当てられている全ての辺で"Union"操作を行う。 子ノードに再帰し、そのうち戻ってきて帰る("out")とき、"Undo"操作を行う。 これにより、常に現在のノードの祖先ノードに割り当てられている辺集合の和集合を管理していることになる。 葉ノードに到達した場合、その葉ノードに割り当てられているクエリは管理しているUnion-findデータ構造に対して行うことにより答えられる。
Union-findデータ構造の操作は$O(\log n)$($n$は頂点数。$n \in O(m)$と仮定できる)時間で実装でき、その操作回数は$O(m \log m)$回であるため、全体で$O(m \log^2 m)$時間となる。
ちなみに
"dynamic connectivity"問題は、オンラインであってもならし$O(\log^2 m)$時間で解くことができる [1]。より高速な方法も提案されている [2]。 ただし、この記事のスコープ外であるためここでは説明しない。
- [1] J Holm, K De Lichtenberg, M Thorup. Poly-logarithmic deterministic fully-dynamic algorithms for connectivity, minimum spanning tree, 2-edge, and biconnectivity.
- [2] M Thorup. Near-optimal fully-dynamic graph connectivity.
演習問題
- Dynamic connectivity contest A - Connect and Disconnect - batched dynamic connectivity である。
- Dynamic connectivity contest D - Bridges: The Final Battle - batched dynamic connectivity と同じようにできる。UnionFindの代わりにどのようなデータ構造を持てばいいか?
- 技術室奥プログラミングコンテスト#2 I - ボス(Boss) - 「要素を区間として見る」ことで削除に対応できる。もっとも、静的なバージョンを解くことが難しいのだが。
- Codeforces Round 333 Div.1 E. A Museum Robbery - クエリを時系列についてSegment Treeのように持つ。
(追加の問題例募集中)