noshi91のメモ

データ構造のある風景

省メモリな区間 add 区間 min

概要

長さ  n の数列  a に対する区間 add と区間 min を時間計算量  \Theta ( \log ( n ) ) で処理する、空間計算量  cn のデータ構造を説明する。 ただし、一連の操作における  \lvert a _ i \rvert の最大値を  X としたとき、 \lbrack -2X , 2X \rbrack の範囲の数を管理するために必要な空間計算量を  c としている。

例: 長さ  10 ^ 5 の数列に  \pm 10 ^ 9 の加算クエリが  10 ^ 6 回ほど行われる状況では、 64 bit 整数  10 ^ 5 個のメモリで処理できる。

構造

簡略化のため、 n 2 の冪の場合をまず考える。 Segment Tree と同様に、配列を用いて二分木の構造を作る。 まず、各ノードに対して対応する区間内の最小値を考える (この値は実際には保持しない)。 続いて、各ノードに対して左子  - 右子を計算し、これを保持する。 葉には子が無いので、データは保持しない。 すると下図のような構造となる。

この右側のデータに加えて全体の最小値も持つことで、Segment Tree に対するものと似たようにクエリの処理が可能となる。 例えば区間 add では、データを適切に更新しつつ、着目しているノードの区間の最小値の増分を返すような関数を設計すると良い。 区間  \lbrack l , r )  x を加算するクエリは以下のようになる

  1. 着目しているノードの区間 \lbrack l , r ) に完全に含まれているなら、 x を返す。
  2. 着目しているノードの区間 \lbrack l , r ) と交差しないなら、 0 を返す。
  3. 左子と右子に対して再帰的に加算クエリを行う。それぞれの返り値から着目しているノードの値を更新し、返り値を計算する。

 n 2 の冪でない場合も、空間計算量が  2n の Segment Tree と同様に完全二分木でない二分木構造を作ればよい。 ノードの個数は  n - 1 になるため、全体の最小値の管理と併せて  n 個の整数を管理することになる。