樹狀陣列詳解

2021-07-23 16:36:19 字數 1651 閱讀 3627

樹狀陣列求區間和的一些常見模型 

樹狀陣列在區間求和問題上有大用,其三種複雜度都比線段樹要低很多„„有關區間求和的問題主要有以下三個模型(以下設a[1..n]為乙個長為n的序列,初始值為全0):

(1)「改點求段」型,即對於序列a有以下操作: 修改操作:將a[x]的值加上c; 求和操作:求此時a[l..r]的和。 

這是最容易的模型,不需要任何輔助陣列。樹狀陣列中從x開始不斷減lowbit(x)(即x&(-x))可以得到整個[1..x]的和,而從x開始不斷加lowbit(x)則可以得到x的所有前趨。**: 

void add(int x, int c)  

int sum(int x)

操作:add(x, c); 操作:sum(r)-sum(l-1)。

(2)「改段求點」型,即對於序列a有以下操作: 修改操作:將a[l..r]之間的全部元素值加上c; 求和操作:求此時a[x]的值。 

這個模型中需要設定乙個輔助陣列b:b[i]表示a[1..i]到目前為止共被整體加了多少(或者可以說成,到目前為止的所有add(i, c)操作中c的總和)。 

則可以發現,對於之前的所有add(x, c)操作,當且僅當x>=i時,該操作會對a[i]的值造成影響(將a[i]加上c),又由於初始a[i]=0,所以有a[i] = b[i..n]之和!而add(i, c)(將a[1..i]整體加上c),將b[i]加上c即可——只要對b陣列進行操作就行了。 

這樣就把該模型轉化成了「改點求段」型,只是有一點不同的是,sum(x)不是求b[1..x]的和而是求b[x..n]的和,此時只需把add和sum中的增減次序對調即可(模型1中是add加sum減,這裡是add減sum加)。**:

void add(int x, int c)  

int sum(int x)

操作:add(l-1, -c); add(r, c); 操作:sum(x)。 

(3)「改段求段」型,即對於序列a有以下操作: 修改操作:將a[l..r]之間的全部元素值加上c; 求和操作:求此時a[l..r]的和。 

這是最複雜的模型,需要兩個輔助陣列:b[i]表示a[1..i]到目前為止共被整體加了多少(和模型2中的一樣),c[i]表示a[1..i]到目前為止共被整體加了多少的總和(或者說,c[i]=b[i]*i)。對於add(x, c),只要將b[x]加上c,同時c[x]加上c*x即可(根據c[x]和b[x]間的關係可得);而add(x, c)操作是這樣影響a[1..i]的和的:若x=i)會將a[1..i]的和加上i*c。也就是,a[1..i]之和 = b[i..n]之和 * i + c[1..i-1]之和。這樣對於b和c兩個陣列而言就變成了「改點求段」(不過b是求字尾和而c是求字首和)。另外,該模型中需要特別注意越界問題,即x=0時不能執行sum_b操作和add_c操作!**: 

void add_b(int x, int c)  

void add_c(int x, int c)

int sum_b(int x)

int sum_c(int x)

inline int sum(int x)

操作: 

add_b(r, c); add_c(r, c); 

if (l > 1) 操作:sum(r) - sum(l - 1)。

樹狀陣列 詳解

對於普通陣列,其修改的時間複雜度位o 1 而求陣列中某一段的數值和的時間複雜度為o n 因此對於n的值過大的情況,普通陣列的時間複雜度我們是接受不了的。在此,我們引入了樹狀陣列的資料結構,它能在o logn 內對陣列的值進行修改和查詢某一段數值的和。假設a陣列為儲存原來的值得陣列,c為樹狀陣列。我們...

樹狀陣列詳解

比如說,我這裡有一組數1,2,3,2,k。我想知道第i到第j的和 mathop sum limits j v i 是多少?樸素演算法 for int k 0 k n k if k i k j ans v k 類似這種的寫法,雖然在某些點值改變時也依然可以計算 我們稱這種問題為動態問題 但複雜度最高到...

樹狀陣列 詳解

由圖可知,原始的陣列是a陣列,樹狀陣列是e陣列。通俗的說 e 1 a 1 e 2 a 1 a 2 e 3 a 3 e 4 a 1 a 2 a 3 a 4 e 5 a 5 e 6 a 5 a 6 e 7 a 7 e 8 a 1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 為什麼是這樣的規律...