資節(結)

前綴和

你們應該早就會了

$$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-nlowbit(n), n&-n
10011(01100+1)=0110100001
10100(01011+1)=0110000100
10000(01111+1)=1000010000

概念

首先忽略根節點,他不會用到,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鄭博軒