はい、Fenwick Trees (Binary Indexed Trees) を次のように適応させることができます。
- O(log n) の特定のインデックスで値を更新する
- O(log n) の範囲の最小値を照会 (償却)
2 つの Fenwick ツリーと、ノードの実際の値を保持する追加の配列が必要です。
次の配列があるとします。
index 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
value 1 0 2 1 1 3 0 4 2 5 2 2 3 1 0
魔法の杖を振ると、次の木が現れます。

両方のツリーで、各ノードがそのサブツリー内のすべてのノードの最小値を表すことに注意してください。たとえば、BIT2 ではノード 12 の値は 0 で、これはノード 12、13、14、15 の最小値です。
クエリ
いくつかのサブツリー値の最小値と 1 つの追加の実ノード値を計算することにより、任意の範囲の最小値を効率的にクエリできます。たとえば、範囲 [2,7] の最小値は、BIT2_Node2 (ノード 2、3 を表す) と BIT1_Node7 (ノード 7 を表す)、BIT1_Node6 (ノード 5、6 を表す)、および REAL_4 の最小値を取得することによって決定できます。 [2,7] のすべてのノードをカバーします。しかし、どのサブツリーを見たいかをどうやって知るのでしょうか?
Query(int a, int b) {
int val = infinity // always holds the known min value for our range
// Start traversing the first tree, BIT1, from the beginning of range, a
int i = a
while (parentOf(i, BIT1) <= b) {
val = min(val, BIT2[i]) // Note: traversing BIT1, yet looking up values in BIT2
i = parentOf(i, BIT1)
}
// Start traversing the second tree, BIT2, from the end of range, b
i = b
while (parentOf(i, BIT2) >= a) {
val = min(val, BIT1[i]) // Note: traversing BIT2, yet looking up values in BIT1
i = parentOf(i, BIT2)
}
val = min(val, REAL[i]) // Explained below
return val
}
両方のトラバーサルが同じノードで終了することは、数学的に証明できます。そのノードは範囲の一部ですが、これまで見てきたサブツリーの一部ではありません。範囲の (一意の) 最小値がその特別なノードにある場合を想像してください。それを調べなければ、アルゴリズムは間違った結果をもたらすでしょう。これが、実際の値の配列を 1 回検索する必要がある理由です。
アルゴリズムの理解を助けるために、ペンと紙を使ってシミュレートし、上記のサンプル ツリーのデータを調べることをお勧めします。たとえば、範囲 [4,14] のクエリは、BIT2_4 (rep. 4,5,6,7)、BIT1_14 (rep. 13,14)、BIT1_12 (rep. 9,10,11、 12) および REAL_8 であり、すべての可能な値をカバーします [4,14]。
アップデート
ノードはそれ自体とその子の最小値を表すため、ノードを変更すると親には影響しますが、子には影響しません。したがって、ツリーを更新するには、変更しているノードから開始し、架空のルート ノード (ツリーに応じて 0 または N+1) まで上に移動します。
あるツリーのノードを更新するとします。
ツリー内の値 v を持つノードを更新するための疑似コード:
while (node <= n+1) {
if (v > tree[node]) {
if (oldValue == tree[node]) {
v = min(v, real[node])
for-each child {
v = min(v, tree[child])
}
} else break
}
if (v == tree[node]) break
tree[node] = v
node = parentOf(node, tree)
}
oldValue は置き換えた元の値であることに注意してください。一方、v は、ツリーを上に移動するときに複数回再割り当てされる可能性があります。
バイナリ インデックス作成
私の実験では、Range Minimum クエリはセグメント ツリーの実装よりも約 2 倍高速で、更新はわずかに高速でした。これの主な理由は、ノード間の移動に非常に効率的なビット演算を使用しているためです。それらはここで非常によく説明されています。セグメント ツリーは非常に簡単にコーディングできるため、パフォーマンス上の利点が本当に価値があるかどうかを考えてみてください。私の Fenwick RMQ の update メソッドは 40 行あり、デバッグに時間がかかりました。誰かが私のコードを必要とする場合は、github に置くことができます。また、ブルート ジェネレーターとテスト ジェネレーターを作成して、すべてが機能することを確認しました。
フィンランドのアルゴリズム コミュニティから、このテーマの理解と実装を手伝ってもらいました。画像のソースはhttp://ioinformatics.org/oi/pdf/v9_2015_39_44.pdfですが、フェンウィックの 1994 年の論文の功績によるものです。