---
# System prepended metadata

title: Binary Indexed Tree

---

# Binary Indexed Tree

![](https://i.redd.it/j7ti7miuwk881.png)

參考[OI Wiki](https://oi-wiki.org/ds/fenwick/) 這是一篇原理以及應用上非常詳盡的題解。

BIT / Fenwick tree / Binary indexed tree 是一個 lightweight 的 range query / point add 的資料結構，在實作難度跟常數上都能做到比線段樹更優，但是是個很玄的資料結構。

兩種操作的 TC 都是 $O(\log n)$

```cpp=
struct BIT {
    int n;
    vector<int> T;
    BIT(int n): n(n), T(n+1) {}
    BIT(const vector<int>& nums): BIT(nums.size()) {
        for (int i=1;i<=n;i++) {
            T[i] = nums[i-1];
            int j = i + (i&-i);
            if (j<=n) T[j] += T[i];
        }
    }
    void add(int i,int v) {
        i++;
        while (i<=n) {
            T[i] += v;
            i += i&-i;
        }
    }
    int query(int i) {
        i++;
        int ans = 0;
        while (i) {
            ans += T[i];
            i -= i&-i;
        }
        return ans;
    }
};
```

還可以寫得更短，

```cpp
struct BIT {
    int n;
    vector<int> t;
	BIT(int n): n(n), t(n+1) {}
    void add(int i,int v) {
        for (i++;i>0;i+=i&-i) t[i] += v;
    }
    int query(int i) {
        int ans = 0;
        for (i++;i<=n;i-=i&-i) ans += t[i];
        return ans;
    }
};
```

我曾痴迷於將他的原理理解透徹，到最後才發現我的腦力還是不夠，Fenwick 的含金量還是太高。不妨看看 Codeforces 紅人 Colin Galen 的影片 
[The Black Box Method: How to Learn Hard Concepts Quickly](https://www.youtube.com/watch?v=RDzsrmMl48I) 約在 3 分時就有提到其實連他本人都沒有完全了解原理，但這也不妨礙他的解題。所以在時機成熟前，就把它當黑箱，能拿來解題就行，不需過度執著原理。

## 二維 query

也就是 https://cses.fi/problemset/task/1739/
要處理子矩陣 query 以及 point add
作為模板，一棵樹如下

```cpp=
struct BIT {
	int m, n;
    vector<vector<int>> t;
	BIT(int m,int n): m(m), n(n), t(m+1,vector<int>(n+1)) {}
	void add(int i_,int j_,int v) {
		for (int i=i_+1;i<=m;i+=i&-i) {
			for (int j=j_+1;j<=n;j+=j&-j) t[i][j] += v;
		}
	}
	int query(int i_,int j_) {
		int ans = 0;
		for (int i=i_+1;i>0;i-=i&-i) {
			for (int j=j_+1;j>0;j-=j&-j) ans += t[i][j];
		}
		return ans;
	}
	int query(int mi,int mj,int Mi,int Mj) {
		return query(Mi,Mj) + query(mi-1,mj-1) - query(Mi,mj-1) - query(mi-1,Mj);
	}
};
```
操作的時間複雜度 $O(\log m \log n)$

## 一些怎樣都好的細節

一個常見的套路是用 BIT 當權值樹，也就是說（做值域壓縮後）把某個區間裡的數值存進 BIT 裡
就可以解類似 count reversed pair 的題目。

比方說 下面的題目：

* [LC reverse-pairs](https://leetcode.com/problems/reverse-pairs/description/)

（本題可以用歸併分治[另見 cdq 分治]或 BIT 求解。因為值域壓縮， $\times 2$ 的要求需要用二分搜達成）

* [LC count-increasing-quadruplets](https://leetcode.com/problems/count-increasing-quadruplets/description/)

(正解是 $O(n^2)$，但可以用 BIT 退而求其次做到 $O(n^2\log n)$)

* [LC minimum-inversion-count-in-subarrays-of-fixed-length](https://leetcode.cn/problems/minimum-inversion-count-in-subarrays-of-fixed-length/)

(CSES 有根本一樣的[題目](https://cses.fi/problemset/task/3223))


更甚者，有一個狂人想到了用兩顆 BIT 來解決 RMQ 問題的做法，常數比 segment tree 低得多，有興趣的話可以參見 [Cf 文章](https://codeforces.com/blog/entry/50903)。

## range add / range query

另外還有一種，使用兩顆 BIT 並且使用差分思想的巧妙方式去實現 $O(\log n)$，從此從 lazy segment tree 中解放，得到更低的常數！

but to do

## 有趣的習題

* 證明`add`, `query`的時間複雜度是$O(\log n)$

For `add`, since `i` grows atleast as fast as `lowbit(i)`, and that if `i=lowbit(i)`, `i` will be doubled in each loop, the loop terminates in $\log n$ time.
For `query`, in each loop, the last bit of `i` gets removed. There are atmost `2\log(i)` bits to remove.

* 證明`query`的有效性。

If `i=1`, we are done. Suppose this hold for any less `i`. Then 
$$\begin{aligned}
query(i) &= T[i] + query(i-lowbit(i)) \\
&= sum(i-lowbit] + sum(0,i-lowbit(i)) \\
&= sum(0,i]
\end{aligned}$$

* 闡述 update tree 跟 interrogation tree的差別？

* 證明 $O(n)$ build方法之有效性。

我也不會

* 證明`add`的有效性。

* 證明或證偽，在 update tree 中，$i$ 有 $ctz(i)$ 個children，其中 $ctz$ 定義為："在二進制中，$i$ 前綴零的個數"

* 在heap及binary indexed tree中，因實作方式不同均存在 $O(n)$ 的及 $O(n\log n)$ 的構造方式。請比較兩者各自將 $O(n\log n)$ 進步到 $O(n)$ 的原理。

