例えば:
int get(int i) {
int res = 0;
while (i) {
res = (res + tree[i]) % MOD;
i -= ( (i) & (-i) );
}
return res;
}
ツリー更新機能:
void update(int i, int val) {
while (i <= m) {
tree[i] = (tree[i] + val) % MOD;
i += ( (i) & (-i) );
}
}
( (i) & (-i) )
を使用して、それらがコードで何をするのか説明してください。
この2つの関数は、 バイナリインデックスツリー(フェンウィックツリー) データ構造の修正された実装です。
これは、 i 変数が異なる値にどのように更新されるかを示す、MikeCATの答えを補足する2つの写真です。
「get」機能:
関数の入力の最大値は、表現を簡単にするために15であると仮定します。
番号が t のノードは、ツリー配列の tree [t] を表します。
get 関数を i に対して呼び出した場合、返される値は tree [i]の合計ですプラス、すべての tree 配列要素の合計。配列内のインデックスは、画像内の i の親です。ただし、ゼロ。
下記は用例です:
get(15) = tree[15] + tree[14] + tree[12] + tree[8]
get(14) = tree[14] + tree[12] + tree[8]
get(13) = tree[13] + tree[12] + tree[8]
get(12) = tree[12] + tree[8]
get(11) = tree[11] + tree[10] + tree[8]
get(10) = tree[10] + tree[8]
get(9) = tree[9] + tree[8]
get(8) = tree[8]
get(7) = tree[7] + tree[6] + tree[4]
get(6) = tree[6] + tree[4]
get(5) = tree[5] + tree[4]
get(4) = tree[4]
get(3) = tree[3] + tree[2]
get(2) = tree[2]
上の図のノードのラベルの番号には、各ノードの親がそのノードラベルから最下位のラベルを引いたものであるというプロパティがあります1(@MikeCATの回答で非常によく説明されています)更新」関数:
図を簡単にするため、 tree 配列の最大長は16であると仮定します。
update 関数は少し複雑です。
val を tree [i] およびすべての tree 要素に追加しますそれらのインデックスは、図のラベル i を持つノードの親であること。
update(16, val) --> tree[16] += val;
update(15, val) --> tree[15] += val, tree[16] += val;
update(14, val) --> tree[14] += val, tree[16] += val;
update(13, val) --> tree[13] += val, tree[14] += val; tree[16] += val;
update(12, val) --> tree[12] += val, tree[16] += val;
update(11, val) --> tree[11] += val, tree[12] += val, tree[16] += val;
update(10, val) --> tree[10] += val, tree[12] += val, tree[16] += val;
update(9, val) --> tree[9] += val, tree[10] += val, tree[12] += val, tree[16] += val;
update(8, val) --> tree[8] += val, tree[16] += val;
update(7, val) --> tree[7] += val, tree[8] += val, tree[16] += val;
update(6, val) --> tree[6] += val, tree[8] += val, tree[16] += val;
update(5, val) --> tree[5] += val, tree[6] += val, tree[8] += val, tree[16] += val;
update(4, val) --> tree[4] += val, tree[8] += val, tree[16] += val;
update(3, val) --> tree[3] += val, tree[4] += val, tree[8] += val, tree[16] += val;
update(2, val) --> tree[2] += val, tree[4] += val, tree[8] += val, tree[16] += val;
update(1, val) --> tree[1] += val, tree[2] += val, tree[4] += val, tree[8] += val, tree[16] += val;
負の値は2の補数を使用して表されると仮定します。この場合、-i
は(~i)+1
(ビットを反転してから1を加算)として計算できます。
たとえば、i = 44
について考えてみましょう。次に、バイナリで、
i = 0000 0000 0000 0000 0000 0000 0010 1100
~i = 1111 1111 1111 1111 1111 1111 1101 0011
-i = (~i)+1 = 1111 1111 1111 1111 1111 1111 1101 0100
(i) & (-i) = 0000 0000 0000 0000 0000 0000 0000 0100
ご覧のとおり、1である最小ビットは(i) & (-i)
を使用して計算できます。
誰もがより一般的な証明も必要とする場合に備えて、
x
の形式がa10であると仮定しますk (ここでは、ビットストリングaの後に1が続き、k個のゼロが続くことを意味します)。
-x
は(定義により)~x + 1
と同じものなので、
そのため、存在すると仮定した右端の1つだけが残っています。
x
の形式に関する仮定は、x = 0
というケースを除外します。この場合、結果は明らかにゼロのままです。