--- tags: 109_FDCS --- # Binary Indexed Tree (Fenwick tree) 又稱樹狀數組, 樹狀陣列, 二元索引樹 用來快速求帶修改的區間和 修改、查詢操作都是 $O(lgn)$ 刻起來比線段樹快而且常數比較小(空間甚至只要 $O(n)$ ) 但區間修改+區間查詢的實現比較不直覺,要用奇怪的功能盡量用線段樹 --- ## 操作 - 單點修改&區間查詢 - 區間修改&單點查詢 --- ## 概念 利用位元的概念把可以把一個前綴和拆成數個數字的加總 以下是在 $n=8$ 時,BIT的示意圖 ![](https://i.imgur.com/NgyHGiw.png) 其中**黑色實線是查詢用的順序**、**紅色虛線是修改用的順序** ### 查詢(前綴和) 對某一個點進行查詢時,沿著**黑色實線**走到 $0$ ,把路過的點值都加總就是所求 ### 修改 對某一點進行修改時,沿著**紅色虛線**走到不能走為止( $\leq n$ ),對路過的點進行修改 --- 為什麼和位元有關係呢 必須先知道**lowbit**是什麼 ### lowbit lowbit是方便接下來操作要用的一個輔助函式 功能是取得一個整數中最低位的 $1$ 所代表的值 e.g. 整數 $21_{(10)}=10101_{(2)}$ $lowbit(21)=1$ ,因為最低位的 $1$ 是最右邊的那個 $1$ ,而那個位置代表的是 $1$ 另一個例子 整數 $20_{(10)}=10100_{(2)}$ $lowbit(20)=4$ ,因為最低位的 $1$ 是右邊數來第三個 $1$ ,而那個位置代表的值是 $4$ --- 知道了lowbit之後 把上面的圖裡的數字用二進位表示的話 ![](https://i.imgur.com/QzF1JnS.png) 可以看出 黑色實線對應的是**減去該點的lowbit** 紅色虛線對應的是**加上該點的lowbit** --- 那為什麼前綴和會是對的呢,不會有重複取到同樣範圍的問題嗎? 對於這個問題我們可以看第三個圖 這次我們把節點的內容換成所代表的區間 ![](https://i.imgur.com/0ErB35R.png) 很像砍掉一半的線段樹,實際上概念差不多 知道理論之後就可以來實作了 --- ## 實作 ### 單點修改&區間查詢 先開一個大小為 $n+1$ 的陣列 ```cpp= constexpr int maxn = 1e5; long long BIT[maxn + 1]; ``` #### lowbit輔助函式 小技巧,背起來即可 :poop: ```cpp=3 inline int lowbit(int x) {return x&-x;} ``` #### 修改 對 $p$ 的位置加上 $x$ **注意** **BIT實作的時候是1-based,換句話說陣列是從1開始** 如果`p`是0-based的話要調整一下 ```cpp=4 void modify(int p, long long x) { p++; while(p <= maxn) BIT[p] += x, p += lowbit(p); } ``` #### 查詢 查詢 $0$ 到 $p$ 的前綴和 ```cpp=9 long long prefix(int p) { long long ret = 0; p++; while(p) ret += BIT[p], p -= lowbit(p); return ret; } ``` --- ### 區間修改&單點查值 把陣列存的值改成差分就好了,自己想 :poop: --- ## 例題 [a164: 0927進階班作業-區間和](http://203.64.191.163/ShowProblem?problemid=a164) ```cpp= constexpr int maxn = 2e5 + 5; using ll = long long; ll bit[maxn]; int n = 0, k, l, r, q; inline int lowbit(int p) {return p & -p;} inline ll prefix(int p) { ll ret = 0; while (p) ret += bit[p], p -= lowbit(p); return ret; }; inline void modify(int p, ll x) { while (p < maxn) bit[p] += x, p += lowbit(p); }; inline ll query(int l, int r) { return prefix(r) - prefix(l - 1); }; inline void solve() { string str; getline(cin, str); stringstream ss(str); while (ss >> k) modify(++n, k); cin >> q; while (q--) cin >> l >> r, cout << query(min(l, r), max(l, r)) << endl; } ``` <!-- :::spoiler CPP RANK 1(2020/12/16) 毒瘤IO優化 BIT code ```cpp= //Input Output #include<iostream> #include<fstream> #include<sstream> #include<streambuf> //C lib #include<cstdlib> #include<cstring> #include<cmath> #include<climits> #include<cctype> //Container #include<array> #include<vector> #include<stack> #include<queue> #include<deque> #include<list> #include<bitset> #include<set> #include<map> #include<unordered_set> #include<unordered_map> //Others #include<tuple> #include<algorithm> #include<functional> #include<numeric> #include<iterator> #include<limits> #include<utility> #include<complex> #include<type_traits> #include<initializer_list> #if __cplusplus > 201703L #include<concepts> #endif using namespace std; #define debug(x) cout << #x << " : " << x << endl namespace abb { using ll = long long; using ull = unsigned long long; template<typename T> using V = vector<T>; template<typename T> using Q = queue<T>; template<typename T> using DQ = deque<T>; template<typename T> using V2d = vector<vector<T>>; template<typename T> using US = unordered_set<T>; template<typename T, size_t s> using A = array<T, s>; template<typename T1, typename T2> using UM = unordered_map<T1, T2>; template<typename T1, typename T2 = T1> using P = pair<T1, T2>; template<typename T, typename Cmp = less<T>> using S = set<T, Cmp>; template<typename T, typename Cmp = less<T>> using PQ = priority_queue<T, Cmp>; template<typename T1, typename T2, typename Cmp = less<T1>> using M = map<T1, T2, Cmp>; } namespace output { template<typename T> ostream& operator<<(ostream& os, const vector<T>& v) { for (const auto& i : v) os << i << ' '; return os; } template<typename T1, typename T2> ostream& operator<<(ostream& os, const pair<T1, T2>& p) { return os << '(' << p.first << ", " << p.second << ')'; } template<typename T> ostream& operator<<(ostream& os, const set<T>& s) { for (const auto& i : s) os << i << ' '; return os; } template<typename T1, typename T2> ostream& operator<<(ostream& os, const map<T1, T2>& m) { for (const auto& i : m) os << i << ' '; return os; } }; namespace rit { struct fast_istream { operator bool() const {return bool(cin);} fast_istream() {cin.tie(nullptr);} } fin; #if __cplusplus > 201703L template<typename T> fast_istream& operator>>(fast_istream& is, T& n) requires integral<T> { #else template<typename T, class = typename enable_if<is_integral<T>::value>::type> fast_istream & operator>>(fast_istream& is, T& n) { #endif while (isspace(cin.rdbuf()->sgetc())) cin.rdbuf()->snextc(); bool sign = false; if (cin.rdbuf()->sgetc() == '-') sign = true, cin.rdbuf()->snextc(); for (n = 0; isdigit(cin.rdbuf()->sgetc());) n *= 10, n += cin.rdbuf()->sbumpc() - '0'; n = sign ? -n : n; return is; } fast_istream& operator>>(fast_istream& is, char& n) { while (isspace(cin.rdbuf()->sgetc())) cin.rdbuf()->snextc(); n = cin.rdbuf()->sbumpc(); return is; } } #define endl '\n' namespace wit { struct fast_ostream { operator bool() const {return bool(cout);} fast_ostream() {cout.tie(nullptr);} } fout; constexpr int buffer_size = 30; #if __cplusplus > 201703L template<typename T> fast_ostream& operator<<(fast_ostream& os, T n) requires integral<T> { #else template<typename T, class = typename enable_if<is_integral<T>::value>::type> fast_ostream & operator<<(fast_ostream& os, T n) { #endif if (!n) {cout.rdbuf()->sputc('0'); return os;} if (n < 0) cout.rdbuf()->sputc('-'), n = -n; static char buffer[buffer_size]; int cnt = buffer_size; for (; n; n /= 10) buffer[--cnt] = n % 10 + '0'; cout.rdbuf()->sputn(buffer + cnt, buffer_size - cnt); return os; } fast_ostream& operator<< (fast_ostream& os, const char& n) { cout.rdbuf()->sputc(n); return os; } } //#define MULTI_TASKCASE using namespace abb; using namespace output; using namespace rit; using namespace wit; #define eb emplace_back inline void init() { } constexpr int maxn = 200005; ll bit[maxn]; int n = 0, k, l, r, q; inline int lowbit(int p) {return p & -p;} inline ll prefix(int p) { ll ret = 0; while (p) ret += bit[p], p -= lowbit(p); return ret; }; inline void modify(int p, ll x) { while (p < maxn) bit[p] += x, p += lowbit(p); }; inline ll query(int l, int r) { return prefix(r) - prefix(l - 1); }; inline void solve() { string str; while (cin.rdbuf()->sgetc() != '\n') { n += cin.rdbuf()->sgetc() == ' '; str += cin.rdbuf()->sbumpc(); } auto tmp = cin.rdbuf(); stringstream ss(str); cin.rdbuf(ss.rdbuf()); for (int i = 0; i <= n; i++) fin >> k, modify(i + 1, k); cin.rdbuf(tmp); fin >> q; while (q--) fin >> l >> r, fout << query(min(l, r), max(l, r)) << endl; } //#define FILEIO main() { ios::sync_with_stdio(false); #ifdef FILEIO fstream filein("in.txt", ios::in); fstream fileout("out.txt", ios::out); cin.rdbuf(filein.rdbuf()); cout.rdbuf(fileout.rdbuf()); #endif init(); int t = 1; #ifdef MULTI_TASKCASE cin >> t; #endif while (t--) solve(); return 0; } ``` ::: -->