實際上,樹狀陣列算是線段樹的小弟角色,樹狀陣列能解決的問題線段樹一定能解決,而線段樹能解決的問題樹狀陣列卻不一定能解決。兩者都是在區間進行操作,但是樹狀陣列是不如線段樹厲害的。但是樹狀陣列的有點就在於常數小,並且短小精悍,手打的時候就幾行**快的一匹。並且lowbit函式的思想非常精妙。
位運算樹狀陣列涉及到大量的位運算,跟二進位制緊密結合。其實就是二進位制中的運算,再換回十進位制,得到結果。
先放個**:
int
lowbit
(int x)
return x&
(-x)
;
非常短小精悍吧!這可以說是樹狀陣列的精髓。而其作用是「二進位制數從低位向高位數,第乙個數字1"作為運算結果。有點難理解,for example:
lowbit(12) = 4
12的二進位制為1100,從低位到高位(右到左),碰見第乙個1時,截斷,只取下面部分作為結果,即:100,十進位制是4。
其實就是二進位制從右向左碰見第乙個1時分開這個數,右邊的部分轉化成十進位制後輸出。這樣,很明顯我們得到的結果必定是1、2、4、8、16等數(1或2^n),同時在變化的過程中不斷迴圈增大。我們只需要提供1-8的lowbit結果就可以了:
根據上面的圖,我們可以做出分析。
如果把lowbit函式的值作為這個位置儲存的區域和,這樣就可以完美覆蓋所有位置。也就是,每乙個數字管理一段區間。就像線段樹一樣。這就是為什麼我們要利用lowbit函式。
我們在維護、使用樹狀陣列的時候,利用累加。原來陣列上利用累加的時候是i++,這樣遍歷一次陣列時間複雜度為o(n),我們既然可以利用lowbit,那麼累加的時候將i++改為i += lowbit(i),這樣雖然我們還是在原陣列上跳躍,但是可以抽象成在乙個樹上按順序遍歷。
我們利用陣列查詢時候o(1)的特點,直接修改對應的數值,即:tree[index] = n。這樣是最開始的一步,接下來就是維護了。我們從index開始,根據lowbit進行遍歷,即:i += lowbit(i),每次迴圈就將tree[i] += n,這樣就保證的了樹狀陣列的更新。放個小**:
void
update
(int index,
int n)
}
操作是非常多變的,具體的操作有具體的寫法,這裡就隨便寫乙個操作了。裡面num這個全域性變數,很明顯是資料的總長度,設定成全域性變數了,當然作為引數由外面傳進來也是可以的。
這裡的區間查詢,可以說查詢的是字首和。例如我們查詢20,得到的是1-20的和,只能得到字首無法得到具體的區間值。不過問題不大,我們可以通過求兩次取差的方法獲取區間值,例如:find(right) - find(left),也可以在某個陣列上取差。
跟更新相反,我們從index開始,利用lowbit進行遞減,就可以一直減到1,就可以求出字首和了。
int
q_sum
(int n)
return ans;
}
#include
#include
#include
#include
using
namespace std;
int tree[
100010];
int num =
100005
;/*
注意這個num,應該是原陣列的長度,可以設成全域性變
量,在最初構造樹狀陣列的時候手動累加。
*/int
lowbit
(int x)
void
update
(int index,
int n)
}int
query_sum
(int n)
return ans;
}int
main()
2020寒假【gmoj1967】【數列】 2020寒假學習筆記03
實驗內容如下 請用指令碼的方式程式設計計算並輸出下列級數的前 n 項之和 sn,直到 sn 剛好大於或等於 q 為止,其中 q 為大於 0 的整數,其值通過鍵盤輸入 例如,若 q 的值為 50.0,則輸出應為 sn 50.416695。在 repl模式下測試執行,測試樣例 q 1時,sn 2 q 3...
樹狀陣列學習
之前寫的題也遇到過用樹狀陣列,當時都是現查現學,而且總是搞不懂,今天又遇到了一道求區間和的題,不管最後是不是用樹狀陣列可以a,但是既然已經想到了這,就打算好好學習一下。可惜之前查到的資料都沒有儲存記錄,所以又重新查了些資料,彙總學習如下 文末附上樹狀陣列的詳細 樹狀陣列主要用到的操作 int low...
樹狀陣列學習
我覺得,樹狀陣列挺重要的就是那個 lower x x x 我說說我的理解吧。每乙個正整數都可以拆分成 2 的某些冪之和,例如 15 8 4 2 1 6 4 2 7 4 2 1 感覺可以解釋lca的倍增跳 那麼轉換成二進位制是什麼樣的呢?15 十進位制 1111 1000 100 10 1 6 十進位...