資節(結)
前綴和
你們應該早就會了
$$a[n] = \sum_{i = 0}^{n} a[i]$$
他可以幹嘛
$$\sum_{i = l}^{r} a[i] = a[r] - a[l-1]$$
你可以 \(O(n)\)預處理
\(O(1)\)計算區間和
二維前綴和
$$a[n][m] = \sum_{i = 0}^{n} \sum_{j = 0}^{m} a[i][j]$$

藍色部分的區間和
\(=a[y_1][x_1]-a[y_1][x_0]-a[y_0][x_1]+a[y_0][x_0]\)
題目
差分
定義
\(a[n] = a[n] - a[n-1]\)
他可以幹嘛
區間改值時可以\(O(1)\)修改
當然查詢的話是\(O(n)\)
BIT
不是比特
區間改值+單點查詢
我可以用前綴何來處理區間改值
可是單點查詢要\(O(n)\)
binary indexed tree
這是一個樹狀資節
最上面會有一個根節點
一個根節點會有一些子節點往下延伸
一個子節點只能有一個父節點
最底下的節點叫葉節點

lowbit
對於一個二進位數字 \(n\)
\(lowbit(n)\)就是把不是最低位的1改成0
例如\(lowbit(0001111000)=1000\)
如何在C++取\(lowbit\)呢?
跟 \(n\) 的負數取bitsiwe and
| n | -n | lowbit(n), n&-n |
|---|---|---|
| 10011 | (01100+1)=01101 | 00001 |
| 10100 | (01011+1)=01100 | 00100 |
| 10000 | (01111+1)=10000 | 10000 |
概念

首先忽略根節點,他不會用到,BIT是1-based
然後你會很容易地觀察到,每個節點存的區間右界,就是他自己
那區間大小呢?
注意到其實就是 \(lowbit(index)\)
然後一個節點的父節點 \(index\)
就是自己的 \(index\) 減掉 \(lowbit(index)\)
區間查詢

例如說查詢\(\sum_{i=3}^{10}a[i]\)
相當於\(\sum_{i=1}^{10}a[i]-\sum_{i=1}^{2}a[i]\)
而a[1]~a[10]的總和
其實就是從節點10開始往父節點跑
然後把經過節點的值加起來
a[1]~a[2]也是同理
單點改值

例如說修改 a[5]
那我需要修改所有涵蓋到 a[5] 的節點
如果我做一個樹,其父節點的區間會涵蓋子節點的區間的話
你會發現父節點的\(index\)
就是子節點的\(index+lowbit(index)\)
所以你就會了
題目
區間修改+單點查詢
BIT存差分
區間修改相當於兩次單點改值
單點修改相當於區間查詢
區間修改+區間查詢
a是原陣列,d是差分陣列
$$\sum_{i=0}^{n-1}a[i] = \sum_{i=0}^{n-1}\sum_{j=0}^{i}d[j]=d[0]\times x + d[1] \times (x - 1)+ d[2] \times (x - 2)......$$
$$=\sum_{i=0}^{n-1}d[i] \times (n - i) = n \times (\sum_{i = 0}^{n - 1}d[i]) - (\sum_{i = 0}^{n - 1}d[i] \times i)$$
從 n 次的區間查詢變成 2 次的區間查詢
所以用 BIT存 \(d[i]\) 與 \(d[i] \times i\)
然後你就會了
二維BIT
概念一模一樣
for (int i = x; i > 0; i -= lowbit(i)) {
for (int j = y; j > 0; j -=lowbit(j)) {
res += BIT[i][j];
}
}for (int i = x; i > 0; i -= lowbit(i)) {
res += BIT[i];
}一維
二維
題目
data_structure
By ck11300768鄭博軒
data_structure
- 18