---
# System prepended metadata

title: Prefix sum 與 BIT
tags: [資料結構, 競程]

---

## Prefix Sum 
前綴和。可用來解決「區間和」的問題。

### 考慮以下問題：
> 存在一個序列 $A$，對於此序列詢問 index $l$ 到 index $r$ 的總和。
> Example: A = [1, 2, 5, 3, 6]
> 詢問 index 2 及 index 4 的總和
> 應回傳 A[2] + A[3] + A[4] = 5 + 3 + 6 = 14

最簡單的方法就是每次處理時，都從 index $l$ 遍歷到 index $r$，但這麼做的時間複雜度會隨著詢問次數上升。
透過觀察不難發現，對於任意詢問 [$l$, $r$]，都能視作 [$0$, $r$] 減去 [$0$, $l - 1$]。
- **舉個例子：**
    $A$ = [1, 5, 6, 2, 5, 9]
    詢問 [$2$, $5$]
    $=$ 6 + 2 + 5 + 9
    $=$ (1 + 5 + 6 + 2 + 5 + 9) - (1 + 5)
    $=$ [$0$, $5$] - [$0$, $2 - 1$]
    $=$ [$0$, $5$] - [$0$, $1$]

### 資料結構
參考以上想法，可以發現單純儲存「 index $0$ 到任意 index 的總和」就能應付所有詢問。而這就是 Prefix Sum 所儲存的東西。

假設 $A$ 有 $n$ 個值，現在先考慮儲存 index $0$ 到 index $i$ 的總和，可以發現所儲存的值會等於 index $0$ 加到 index $(i - 1)$ 的值**再加上** index $i$ 的值。以此類推，我們可以發現儲存的值會等於當前值加上原陣列的值，因此構建 Prefix Sum 時只需遍歷 $A$ 一遍就行。

由於需要儲存的資料為 index $0$ 到 $i$ 的和，而 $i$ 小於等於 $n$ ，因此至少需要 $n$ 個空間，再加上一開始的和是 $0$，總共需要 $n + 1$ 的空間（這邊也可以是 $n - 1$ 個空間，看需不需要儲存一開始的 $0$ 及最後的 $sum(A)$）


範例程式碼如下
```cpp=
int A[] = [1, 5, 6, 2, 5, 9];
vector<int> preSum(A.size() + 1, 0);    // n+1 的空間，預設值為 0
for(int i = 0; i < A.size(); i++){
    preSum[i + 1] = preSum[i] + A[i];   //現在的值會等於前一個值加上 A 現在的值
                                        //因為 A 的索引最小是 0 ，而如果要符合上面
                                        //的算式，那麼 Prefix Sum 就必須用 1-index
}
```
若要搜尋 [$l$, $r$]
```cpp=
int ans = preSum[r] - preSum[l];
```

### 參考題目
[303. Range Sum Query - Immutable](https://leetcode.com/problems/range-sum-query-immutable/description/)
## Binary Index Tree (BIT)
二元索引樹，又稱**樹狀數組**。可用來解決「動態區間和」的問題。

### 考慮以下問題：
> 存在一個序列 $A$，所有元素皆有預設值 $0$；對於此序列會有以下動作：
> 1. 把 $A_i$ 加上指定值 $v$
> 2. 詢問 [$0$, $r$] 區間和。

如果在每次修改都去更新 Prefix Sum 的元素的話，時間複雜度跟一開始講到的一樣會隨著詢問次數上升。

而 BIT 與 preSum 類似，都儲存了某個區段的區間和，但與 preSum 不同的是，在一次修改後的更新， preSum 需要 $O(n)$ 的複雜度，而 BIT 只需要 $O(log\ n)$

### 資料結構
以 $B$ 來表示 BIT 這個資料結構，在實作中可以直接使用 array 作為 BIT 。由於使用 `1-index` ， $B[0]$ 不會被使用到，因此它的空間與 prefix Sum 一樣是 $n + 1$。

<font style="background-color: GreenYellow">$B$ 的每個 index $i$ 都代表了往下數 $lowbit(i)$ 個數的區間和。</font>其中 $lowbit(i)$ 指的是 $i$ 轉成二進位後，把最低位的 $1$ 以外的 $1$ 都變成 $0$ 所代表的值。以 $(4)_{10}$ , $(14)_{10}$ , 及$(15)_{10}$ 為例子：

- $(4)_{10}$：
    - 計算 $lowbit(4)$
        $(4)_{10} = (100)_2$
        $lowbit(4) = (100)_2 = 4_{10}$
    - 得出 index $4$ 的值是往下數 $lowbit(4)$ 也就是 **4 個值**的總和。
        index $4$ 的值是 index $(100)_2$ + index $(011)_2$ + index $(010)_2$ + index $(001)_2$
        以範圍表達就是 $[1, 4]$

- $(14)_{10}$：
    - 計算 $lowbit(14)$
        $(14)_{10} = (1110)_2$
        $lowbit(14) = (0010)_2 = 2_{10}$
    - 得出 index $14$ 的值是往下數 $lowbit(14)$ 也就是 **2 個值**的總和。
        index $14$ 的值是 index $(1110)_2$ + index $(1101)_2$
        以範圍表達就是 $[13, 14]$

- $(15)_{10}$：
    - 計算 $lowbit(15)$
        $(15)_{10} = (1111)_2$
        $lowbit(15) = (0001)_2 = 1_{10}$
    - 得出 index $15$ 的值是往下數 $lowbit(15)$ 也就是 **1 個值**的總和。
        index $15$ 的值是 index $(1111)_2$
        以範圍表達就是 $[15, 15]$


<font style="background-color: Orange">$B$ 的 index $i$ 代表了 [$i - lowbit(i) + 1$, $i$] 的區間和</font>，像是 index $14$ 的值是[$14 - 2 + 1$, $14$] ，也就是 [$13$, $14$]。而 index $4$ 或 index $15$ 的值可以根據上面的方法算出來。

再觀察<font style="background-color: GreenYellow">上面提到的特性</font>，index $i$ 所包含的範圍只會在下一個 lowbit() 嚴格大於 lowbit(i) 的 index 包含到。以剛剛的 $(4)_{10}$ , $(14)_{10}$ , 及$(15)_{10}$ 為例子：

- $(4)_{10}$：
    以範圍表達就是 $[1, 4]$
    繼續找範圍被覆蓋到的 index：
    - $(5)_{10} = (4)_{10} + (1)_{10}$：
        - 範圍是 $[5, 5]$
    - $(6)_{10} = (4)_{10} + (2)_{10}$：
        - 範圍是 $[5, 6]$
    - $(7)_{10} = (4)_{10} + (3)_{10}$：
        - 範圍是 $[7, 7]$
    - $(8)_{10} = (4)_{10} + (4)_{10}$：
        - **範圍是 $[1, 8]$
            找到了**

- $(14)_{10}$：
    以範圍表達就是 $[13, 14]$
    繼續找範圍被覆蓋到的 index：
    - $(15)_{10} = (14)_{10} + (1)_{10}$：
        - 範圍是 $[15, 15]$
    - $(16)_{10} = (14)_{10} + (2)_{10}$：
        - **範圍是 $[1, 16]$
            找到了**

- $(15)_{10}$：
    以範圍表達就是 $[15, 15]$
    繼續找範圍被覆蓋到的 index：
    - $(16)_{10} = (15)_{10} + (1)_{10}$：
        - **範圍是 $[1, 16]$
            找到了**


可以發現<font style="background-color:yellow;">**每個 $i$ 的範圍會被 $i + lowbit(i)$ 所包含**</font>，因此在更新 $i$ 時，也要同時更新 $i + lowbit(i)$。而同時因為 $0$ 並不往下包含任何數字，因此 $BIT$ 整體是以 $1$-index 來做處理。

而對於 $lowbit(i)$ ，則可以用以下算式來處理：
$$
lowbit(i) = i\ \&\ -i
$$
因為負數會反相加一的緣故，所以可以保留最低位元的 $1$ ，這剛好就是我們要求的 $lowbit(i)$。

對於範例問題，應該要有 `update(index, target)` 及 `query(index)`，而由於 BIT 可以處理動態區間和的問題，在初始化時通常是初始化一個全為 0 的空陣列，再根據題目的 $A$ 一個一個加上去更新每個需要更新的值。

#### update
`update` 需更新會用到此範圍所有 bit 的節點，依照<font style="background-color:yellow;">上述特性</font>，假設需要更新的值為 $B[index]$，那麼 $B[index + lowbit(index)]$ 的值也要更新，以此類推。

##### 時間複雜度 $O(log\ n)$ 
因為 lowbit 為最小位的 1 ，每遞迴一次就會進位(二進制) ， 所以遞迴次數最多為 $log\ n$

#### query
`query` 所求為 $[1, index]$ 的總和，<font style="background-color: orange">由於 index 所包含的部分不一定會覆蓋到 index $1$ </font>，我們可以先求出 index 所覆蓋的部分，再求出 $[1, index - lowbit(index)]$ 所覆蓋的部分。因此，$[1, index]$ 的總和可以拆分為
$$
[1, index - lowbit(index)] + [index - lowbit(index) + 1, index]
$$
而 $[index - lowbit(index) + 1, index]$ 就是 $BIT[index]$ 所表達的值，因此整個算式能夠改寫成
$$
[1, index - lowbit(index)] + BIT[index]
$$
而前面的 $[1, index - lowbit(index)]$ 也可以透過這個算式遞迴算出[1, index]的總和。

##### 時間複雜度 $O(log\ n)$ 
因為 lowbit 為最小位的 1 ，每遞迴一次就會扣掉最小位的 1 ， 所以遞迴次數最多為 $log\ n$

範例程式碼：
```cpp=
int lowbit(int value){
    return value & (-value);
}

void update(int index, int target){
    while (index < B.size()){
        B[index] += target;
        index += lowbit(index);
    }
}

int query(int index){
    int sum = 0;
    
    while (index > 0){
        sum += B[index];
        index -= lowbit(index);
    }
    return ans;    
}
```
### 參考題目
[307. Range Sum Query - Mutable](https://leetcode.com/problems/range-sum-query-mutable/description/)
