有一個序列
,一開始都是 0,有 筆操作,每筆操作都是以下其中一種:
- 把
加上 - 詢問
區間和
雖然帶修改前綴和也可以用線段樹來做,不過有一個更適合用在前綴和的可修改結構——樹狀數組/二元索引樹(Binary Indexed Tree,簡稱 BIT)。BIT 的常數小,實作也更簡單。
BIT 的空間只要
BIT 的每一個位置表示的值根據它的位置編號而定,
所以說,
要得到 x&-x
,也就是把它和它的相反數取 bitwise and,因為 -x
會是 x
的補數加 x
在最低位的是
節點
那麼 BIT 畫成樹是什麼樣子?其實它並不是二元樹,binary 指的是二進位。
節點
在知道這些性質後,就可以來討論怎麼處理詢問了。
如果現在要修改一個位置
所以只要一直把
實作相當簡單: 1
2
3
4
5
6void modify(int pos, int x){ // 把 pos 改成 x
// n 是序列大小
for(; pos <= n; pos += lowbit(pos)){
修改節點 pos 的答案;
}
}
可以發現
要查詢以
區間以
實作也非常簡單: 1
2
3
4
5
6
7type query(int pos){
type ans;
for(; pos > 0; pos -= lowbit(pos)){
更新ans;
}
return ans;
}
同樣地,
如果有初始值的話,就把每一個元素分別 modify 就好了,複雜度是
BIT 最基礎的應用就是拿來算前綴和,因為和是一種「可返回」的東西,減去本來的值在加上新的值就可以得到新的和,而有了前綴和就也可以算區間和了,且它比線段樹好寫很多。
BIT 前綴和和差分結合使用,就可以做到
BIT 也可以區間修改、區間查詢前綴和。先算出要做的序列
然後可以把它化成:
那麼只要用兩個 BIT,一個維護
至於 BIT 可不可以做最大最小值這種「不可返回」的操作呢?如果在求最大值的數字時,把其中一個位置改小,那麼是沒辦法單靠被修改的位置就得出新的最大值的,因此如果要用 BIT 做最大值,數字只能被改大,做最小值,數字只能被改小。