樹狀陣列學習筆記

2021-10-25 07:26:51 字數 3272 閱讀 3554

樹狀陣列(binary index tree, bit)也是很多人心中最簡潔優美的資料結構之一。最簡單的樹狀陣列支援兩種操作,時間複雜度均為 :

當然,樹狀陣列能維護的不侷限於加法,支援的操作也不止這兩種,甚至有大佬能用樹狀陣列實現平衡樹,但這篇筆記不會深入討論(因為我也還不是很懂)。

回顧一下,我們說,我們要實現兩種操作:單點修改區間求和。對於普通陣列而言,單點修改的時間複雜度是 o(1

)o(1)

o(1)

,但區間求和的時間複雜度是 o(n

)o(n)

o(n)

。當然,我們也可以用字首和的方法維護這個陣列,這樣的話區間求和的時間複雜度就降到了 o(1

)o(1)

o(1)

,但是單點修改會影響後面所有的元素,時間複雜度是o(n

)o(n)

o(n)

。程式最後跑多長時間,是由最慢的一環決定的,因此現在我們希望找到這樣一種折中的方法:無論單點修改還是區間查詢,它都能不那麼慢地完成。

注意到對[a,

b]

[a,b]

[a,b

]進行區間查詢只需查詢 [1,

b]

[1,b]

[1,b]和[1,

a)

[1,a)

[1,a

)然後相減即可(字首和就是這樣進行區間查詢的),所以我們可以把區間查詢問題轉化為求前n項和的問題。

關於陣列的維護,有個很自然的想法:可以用乙個陣列維護若干個小區間,單點修改時,只更新包含這一元素的區間;求前n項和時,通過將區間進行組合,得到從1到n的區間,然後對所有用到的區間求和。實際上,設原陣列是 a

aa,如果 c

ic_i

ci​維護的區間是[ai

,ai]

[a_i,a_i]

[ai​,a

i​] ,此結構就相當於普通陣列(還浪費了一倍記憶體);如果c

ic_i

ci​維護的區間就是[1,

ai

][1,a_i]

[1,ai​

],此結構就相當於字首和。

現在我們試圖尋找一種結構,一方面,單點修改時需要更新的區間不會太多;另一方面,區間查詢時需要用來組合的區間也不會太多。

樹狀陣列就是這樣一種結構,它巧妙地利用了二進位制(實際上,樹狀陣列的英文名bit,直譯過來就是二進位制下標樹)。例如11,轉化為二進位制數就是 (

1011)2

(1011)_2

(1011)

2​,如果我們要求前11項和,可以分別查詢 (

(0000)2

,(

1000)2

]((0000)_2,(1000)_2]

((0000

)2​,

(100

0)2​

]、 (

(1000)2

,(

1010)2

]((1000)_2,(1010)_2]

((1000

)2​,

(101

0)2​

]以及 (

(1010)2

,(

1011)2

]((1010)_2,(1011)_2]

((1010

)2​,

(101

1)2​

]的和再相加。這三個區間怎麼來的呢?其實就是不斷地去掉二進位制數最右邊的乙個1的過程。

我們定義,二進位制數最右邊的乙個1,連帶著它之後的0為 low

bit(

x)

lowbit(x)

lowbit

(x)(稍後再來看如何實現)。那麼我們用c

ic_i

ci​維護區間((a

i−lo

wbit

(ai)

,ai]

((a_i-lowbit(a_i),a_i]

((ai​−

lowb

it(a

i​),

ai​]

,這樣顯然查詢前n項和時需要合併的區間數是少於log⁡2

n\log_2^n

log2n​

的。那麼如何更新呢,大家會發現更新就是乙個「爬樹」的過程。一路往上更新,直到maxn(樹狀陣列的容量)。

每一步都把從右邊起一系列連續的1變為0,再把這一系列1的前一位0變為1。這看起來像是乙個進製的過程對吧?實際上,每一次加的正是 low

bit(

x)

lowbit(x)

lowbit

(x)。這樣,我們更新的區間數不會超過 log⁡2

maxn

\log_2^

log2ma

xn​。乙個能以 o

(log⁡n

)o(\log n)

o(logn

)時間複雜度進行單點修改和區間查詢的資料結構就誕生了。

lowbit怎麼算?如果一位一位驗證的話,會形成額外的時間開銷。然而,我們有這樣神奇的乙個公式:

l ow

bit(

x)=(

x)

lowbit(x)=(x)

lowbit

(x)=

(x) & (−x

)(-x)

(−x)

我們需要知道,計算機裡有符號數一般是以補碼的形式儲存的。-x相當於x按位取反再加1,會把結尾處原來1000…的形式,變成0111…,再變成1000…;而前面每一位都與原來相反。這時我們再把它和x按位與,得到的結果便是low

bit(

x)

lowbit(x)

lowbit

(x)。

int tree[maxn]

;inline

void

update

(int i,

int x)

inline

intquery

(int n)

inline

intquery

(int a,

int b)

初始化的時候,我們只需要update每個點的初始值即可。

(hdu p1166)敵兵布陣

(洛谷p1908) 逆序對

學習筆記 樹狀陣列

以下 為樹狀陣列最常用的幾個操作 1.low bi t mathrm lowbit function lowbit x longint int64 begin exit x and x end 2.單點修改 procedure replace x,y int64 var i int64 begin ...

樹狀陣列學習筆記

在學習完了線段樹後,聽說樹狀陣列能寫的題,線段樹都能做,所以一直沒有詳細的學習樹狀陣列 直到碰到了一道卡線段樹的題目,因為線段樹運用了很多遞迴,所以常數比較大,容易被卡 現在總結一下樹狀陣列 1 樹狀陣列個人認為就是字首和演變而來的 2 單點更新 當你要更新某個點的值時,你要從下面到上面依次更新過去...

樹狀陣列學習筆記

參考自 0.介紹 來自wikipedia 樹狀陣列,又稱二分索引樹 binary indexed tree,bit 用於高效計算數列的字首和.它可以以 的時間得到 並同樣以 對某項加乙個常數。1.資料結構定義 存放原始資料元素的陣列a i.e.int a 樹狀陣列 s i.e.int s 注意 原始...