# BIT ## infro 又稱樹狀數組,是一種以二進位為基礎的資料結構 功能和線段數差不多 但時間複雜度的常數小,使用的記憶體也較小,實作難度也低 ## 先備知識 lowbit lowbit就是取二進位中,位元為1的最小位元的值(其實就是最右邊的1) EX:$6\implies 0110 \implies lowbit = 2$ 求法是$x \& -x$ $(0110)₂ → (1001)₂+1 → (1010)₂ → (0110)₂\&(1010)₂=2$ $x = 12 = (1100)₂ → lowbit = 4$ $x = 8 = (1000)₂ → lowbit = 8$ ## 結構 ![](https://i.imgur.com/yMdv7D9.png) 我們可以從圖看到每個節點的父節點都是自己本身加上自己的lowbit 且每個節點都會把他所有子孫的節點存起來 其實就是每個節點$i$都存著陣列$(i - lowvit(i), i]的值$ ## 實作 ### lowbit ```cpp= int lowbit(int x){return x & -x}; ``` ### build 更新就是跑過所有節點 假設跑到i節點就將會存到i節點的所有祖先都把節點i加起來 ```cpp= void build(){ for(int x = 1; x <= n; x++) { int k = x; while(k <= maxn) bit[k] += arr[i], k += lowbit(k); } } ``` ### query 這裡查詢的是前綴和 所以不只回傳bit[i] 還要這個bit沒有存到且在i前面的值,所以要在用遞迴或迴圈的方法找$x - lowbit(x)$的值 ```cpp= int query(int x){ if (x) return query(x - lowbit(x)) + bit[x]; return 0; } int query(int x){ int res = 0; while(x > 0){ res += bit[x]; x -= lowbit(x); } return res; } ``` ### update 和build差不多只是只要操作一個節點 ```cpp= void update(int x, int val) { if (x > maxn) return; bit[x] += val; update(x + lowbit(x), val); } void update(int x, int val){ while(x <= maxn) bit[x] += val, x += lowbit(x); } ``` ### 區間查詢 & 單點修改 ```cpp= #include<bits/stdc++.h> using namespace std; const int N = 2e5; int n; int bit[N + 1], arr[N + 1]; int lowbit(int x) {return x & -x;} void build(){ for(int x = 1; x <= n; x++) { int k = x; while(k <= N) bit[k] += arr[x], k += lowbit(k); } } int query(int x){ while(x) return query(x - lowbit(x)) + bit[x]; return 0; } void update(int x, int val){ while(x <= N) bit[x] += val, x += lowbit(x); } int main(){ cin >> n; for(int i = 1; i <= n; i++) cin >> arr[i]; build(); int T, op, l, r, idx, x; cin >> T; while(T--){ cin >> op; if(op){ cin >> l >> r; cout << query(r) - query(l - 1) << '\n'; } else{ cin >> idx >> x; update(idx, x); } } return 0; } ``` ### 單點查詢 & 區間修改 使陣列arr[i]存兩項的差值,也就是arr[i] -= arr[i - 1] 這樣只要修改邊界兩邊的值就可以做到區間修改 但查詢就會變成只能單點查詢,且query出來的值不再有前綴和的特性 ```cpp= #include<bits/stdc++.h> using namespace std; const int N = 2e5; int n; int bit[N + 1], arr[N + 1]; int lowbit(int x) {return x & -x;} void build(){ for(int x = 1; x <= n; x++) { int k = x; while(k <= N) bit[k] += arr[x], k += lowbit(k); } } int query(int x){ while(x) return query(x - lowbit(x)) + bit[x]; return 0; } void update(int l, int r, int val){ while(l <= N) bit[l] += val, l += lowbit(l); while(r <= N) bit[r] -= val, r += lowbit(r); } int main(){ cin >> n; for(int i = 1; i <= n; i++) cin >> arr[i]; for(int i = n; i; i--) arr[i] -= arr[i - 1]; build(); int T, op, l, r, idx, x; cin >> T; while(T--){ cin >> op; if(op){ cin >> idx; cout << query(idx) << '\n'; } else{ cin >> l >> r >> x; update(l, r + 1, x); } } return 0; } ```