ひとりで勝手にはじめた蟻本読書会 セグメント木 遅延評価セグメント木で区間の総和・最大値(最小値)を素早く計算する の続きです。

フェニック木とセグメント木の違い

Binary Indexed Tree(BIT 別名 フェニック木)は区間の和に対するクエリ(Range Sum Query)を効率的に処理するデータ構造です。区間の和は初項からという制約がありますが、任意区間(a, b)であっても sum(b) – sum(a – 1) で計算可能なので任意区間の総和を求めるときにも使うことができます。

区間和は初項からしか計算しないという条件が加わるとセグメント木はどうなるでしょうか?

セグメントでは配列 A の和は以下のように格納されていました。

A[0] から A[x] の和は以下のノードを使えば計算できます。

ここで使われていない部分があることに気が付きます。

使われていないのであれば省略できます。そのぶんメモリーを節約することができるのです。

配列の添字は以下のようにします。0 は使いません。区間の右端で番号づけをするのです。使用する配列の長さは管理したい配列の長さに 1 を足したものです。セグメント木とちがって 2 の N 乗にする必要はありません。

更新は下から順番におこないますが、つぎに更新処理をする区間は番号に区間の長さを足すことで求められます。そしてbit[x] が管理する区間の長さは,x の最も下の立っているビット x & -x で求めることができます。

bit[1] を更新したら 1 + (1 & -1) = 2 となり、bit[2] を更新したら 2 + (2 & -2) = 4、bit[4] を更新したら 4 + (4 & -4) = 8 となるのです。添字と bit のサイズ -1 が同じになったのでここで終了です。

Bitクラスを定義する

以下は配列のサイズを指定したら添字を指定して値を増加させる、添字を指定して[0, index]までの総和を求めるクラスを定義したものです。

B – Fenwick Tree

これで以下を解いてみることにします。

B – Fenwick Tree

長さ N の数列 a[0], a[1], …, a[N-1] に Q 個のクエリが飛んできます。処理してください。

0 p x ⇒ a[p] の値を x 増加させる
1 l r ⇒ a[l] から a[r-1] までの総和を出力する

入力されるデータ
N Q
a[0] a[1] … a[N-1]
Query 0
Query 1
Query Q-1

(ただし、1 ≦ N, Q ≦ 500,000
0 ≦ a[i], x ≦ 10^9 )

C – データ構造

C – データ構造

数の集合 S に対する以下のクエリを処理してください。

タイプ 1 : S に数 X を追加する。
タイプ 2 : S に含まれる数のうち X 番目に小さい数を答え、その数を S から削除する。

入力されるデータ
Q
T_1 X_1
T_2 X_2
:
T_Q X_Q

ただし、
Q はクエリの個数 Q(1 ≦ Q ≦ 200,000)
T_i はクエリのタイプ 1 または 2
1 ≦ X_i ≦ 200,000

「X 番目の数」とは「それ以下の数が X 個であるような数のうち最小のもの」です。そのため「ある数 X 以下の数がいくつあるか」を高速に求めることができるデータ構造があればあとは二分探索で求めることができそうです。

配列 A を定義し、S に数 X を追加する動作を A[X] をインクリメントすることにします。すると A[0] ~ A[X] の総和は X 以下の数の個数となります。フェニック木を使えばうまくいきそうです。

区間全体に対して値を加算する処理

上記の方法だと特定の要素にしか値を加算することができません。区間全体に対して値を加算するためにはどうすればよいでしょうか?

差分に着目します。BITオブジェクトを 2 個使うことでできるようになります。