詳解樹狀陣列三種模型

2021-07-03 10:22:51 字數 4165 閱讀 2739

首先說明下:最後的最大值模型的**沒有測試,不過應該是沒問題的。其它三個更新求和的模型的**借鑑於網上的各個博文,應該是沒問題的。其中前兩個模型的**已測試無誤。

樹狀陣列與線段樹在思想上很類似的一種資料結構,它比線段樹更簡潔,但它的適用範圍也小了些。

提供一篇博文,詳解樹狀陣列的:

樹狀陣列是乙個可以高效的進行區間統計的資料結構,它的本質即為將區間和通過某個特定的函式分為幾段存到陣列中,樹狀陣列中的元素值對應原序列某一段特定區間的區間和,所以它可以進行區間查詢和更新點,所以樹狀陣列常見於區間求和問題。

首先來介紹一下樹狀陣列的原理。首先,每個整數都能以二進位制的方式表示,即由一段連續的0和1表示,0代表沒有,1代表有。如:13=1101(2)。記lowbit表示為x的二進位制表示中的最後乙個1。通過函式lowbit(x)=x&(-x )可以求得該數。這樣,我們建立乙個一維陣列tree,其中tree[i]表示[i-lowbit(i)+1,i]這個區間內所有元素的和。這樣,我們就寫出了乙個樹狀陣列。具體的記錄方式如下圖所示。

該錶也反映了樹狀陣列的記錄方式,f[i]為i的字首和,即[1,i]內的元素和,c[i]為第i位上元素的值,tree[i]為樹狀陣列中第i位記錄的值,綜合該**和上圖,我們可以仔細理解一下樹狀陣列的記錄方式。

查詢區間和:

在建立好了樹狀陣列之後,我們要進行應用。首先介紹怎麼查詢字首和,即對於i,求[1,i]的區間和。由樹狀陣列定義可得,sum[1,1111(2)]=tree[1111]+tree[1110]+tree[1100]+tree[1000]。顯然,這是乙個很方便的操作,我們的陣列設計的就是這樣:tree[1111]=f[1111],tree[1110]=f[1101]+...+f[1110],tree[1100]=f[1001]+...+f[1100],tree[1000]=f[0001]+...+f[1000]。f[i]為原序列中下標為i對應的值。所以求字首和的**是

int query(int i)

return sum;

}

所以求[l,r]上的區間和sum=query(r)-query(l-1);操作的次數是二進位制中1的個數,所以複雜度是o(log n)。

接下來介紹更新點,由於樹狀陣列中儲存的是某一段特定區間的區間和,所以在更新點時,所有包括了這個點的區間和都要更新。由於我們是向上儲存的。所以要更新該點以及後面中包括了該點的區間和。**如下:

void update(int i, int val)

}

至此介紹完了樹狀陣列的查詢區間和更新點,這是樹狀陣列最基本的用途。

接下來我們來介紹樹狀陣列的其他幾個模型。

第二種模型——樹狀陣列的更新區間和查詢點模型。

現在我們假設原序列是序列a,我們要維護的是a序列,要對a進行更新區間和查詢點。然後我們建立乙個初值全為0的b序列。b(i)表示我們對[1,i]整體總共被加了多少。b(i)=c表示我們把a(1)到a(i)的值全部都加上了c。如我們將[1,i]上的值都加上a,則b(i)+=a,如果我們又要對[1,i]上的值都加上-b,則b(i)-=b。設函式add(x,c)表示將[1,x]上的值都加上c,則顯然當且僅當x>=i時,該操作會對a(i)造成影響。又如果我們的a(i)的初始值為0,則a(i)=b(i)+...+b(n)!而對於區間更新,將b(i)加上c即可。

現在我們假設序列a的初值全為0,所以現在如果我們要將a的[l,r]上的值加上c,只需b(r)+=c,b(l-1)-=c。如果我們要查詢a(i)的值,a(i)=b(i)+...+b(n)。

現在我們要維護a序列,只需維護b序列即可,對a序列更新區間即相當於對b序列更新兩個點,對a序列查詢點即相當於對b序列查詢區間。所以我們對b序列的操作就是查詢區間和更新點,所以我們可以將b序列建成一棵樹狀陣列,這樣我們通過樹狀陣列也實現了更新區間和查詢點,只不過這個樹狀陣列的元素不是我們所求序列的元素值,而是它們之間有乙個類似於函式的關係。

但是,由於我們原本的樹狀陣列是查詢的字首和,而在本模型中我們查詢的是字尾和b(i)+...+b(n),所以,我們之前的樹狀陣列是向下查詢向上更新,本模型中就是向上查詢向下更新,其實就是樹狀陣列中每乙個元素儲存的區間變了,之前是從該點向下擴充套件的乙個區間,而現在是從該點向上擴充套件的乙個區間。

更新區間**

void update(int l, int r, int val)

查詢點的**

int query(int i)

return sum;

}

第三種模型——樹狀陣列的更新區間和查詢區間模型

這個模型就相對複雜一些,除了原來的序列a之外,還需要b和c兩個輔助序列。

設a序列為初始的序列,b序列同第二種模型,b(i)表示對區間[1,i]整體總共被加了多少。c(i)表示[1,i]整體每個數被加的數的總和。即對於add(x,c)(把區間[1,x]上的元素都加上c),b(x)+=c,c(x)+=c*x。

而add(x,c)是這樣影響[1,i]的區間和的:若x=i),會將a[1,i]上的區間和加上i*c。所以[1,i]的區間和=(b(i),...,b(n))*i加上c(1),...,c(i-1)。

那為什麼我們這樣求a[1,i]的區間和呢?,我們已知[1,i]的元素整體都被加上了b(i),...,b(n)這些數,然後還有我們的一些更新的操作只影響到了[1,i]裡的部分元素。如我們想求區間[1,10]的區間和,那如果我們之前對區間[1,3][1,7]等[1,10]的子集進行了區間更新,那麼這些操作就只會影響到區間裡的部分元素,所以我們也不能用b(3)*10,b(7)*10,我們應該寫成b(3)*3,b(7)*7,但這樣就意味著我們 需要記錄之前進行了哪些更新的操作,並且我們還要判斷哪些更新操作是對所求區間的子區間進行的更新操作,但這樣的時間複雜度和空間複雜度就大大增加了,為了使我們的演算法保持優秀,所以我們才有了c這個輔助陣列,對於那些所求區間的子集裡的更新操作,我們都可以用c(1),c(2),...,c(i-1)來記錄,比如c(2),表示我們對[1,2]這個區間進行的更新操作,但如果我們對這個區間沒有進行更新操作,那c(2)就為0,所以c(1),c(2),...,c(i-1)就可以將[1,i]的子區間裡進行的區間更新操作記錄完全。當然,我們對於那些包含區間[1,i]的區間更新操作,就不能用c()來求,因為c()是記錄的對應整個大區間的區間的所有值的更新的和,我們所求的只是其中的一部分,所以就是用(b(i),...,b(n))*i來記錄。所以我們想要求區間和,就分成了這兩部分。所以[1,i]的區間和=(b(i),...,b(n))*i加上c(1),...,c(i-1)。

對於區間更新操作,b陣列和c陣列都要更新。對於區間查詢操作,我們也是對b陣列和c陣列都要查詢,**如下:

void update_b(int x, int c)

}void update_c(int x, int c)

}

int sum_b(int x)

return s;

}int sum_c(int x)

return s;

}

對於更新區間,使[l,r]上都加上c,則執行update(r,c)和update(l-1,-c)。對於查詢區間[l,r]的區間和,則等於sum_b(r)*r+sum_c(r)-sum_b(l-1)*(l-1)-sum_c(l-1)。

至此樹狀陣列常見的三種模型已經介紹完畢。

我們再來介紹樹狀陣列的儲存最大值的模型。

其實就是樹狀陣列第一種模型的所儲存的東西發生了變化,儲存的不再是區間和,而是這個區間內的最大值,由於第一種模型是改點求段型,所以我們這個也是改點求段型,不難發現,對於儲存最大值,進行改點求段還是很方便的。

查詢區間[1,i]的最大值的**:

int query(int i)

{ int max = 0x7fffffff;

while (i)

{ if (max

因為樹狀陣列中已經是儲存的最大值,其他的值都已經沒有,所以,只能查詢[1,i]區間的最大值。

更新點的**:

void update(int i, int val)

{ while (i <= n)

{ if (tree[i]

樹狀陣列區間求和三種模型

樹狀陣列在區間求和問題上有大用,其三種複雜度都比線段樹要低很多 有關區間求和的問題主要有以下三個模型 以下設a 1.n 為乙個長為n的序列,初始值為全0 1 改點求段 型,即對於序列a有以下操作 修改操作 將a x 的值加上c 求和操作 求此時a l.r 的和。這是最容易的模型,不需要任何輔助陣列。...

樹狀陣列區間求和三種模型

樹狀陣列在區間求和問題上有大用,其三種複雜度都比線段樹要低很多 有關區間求和的問題主要有以下三個模型 以下設a 1.n 為乙個長為n的序列,初始值為全0 1 改點求段 型,即對於序列a有以下操作 修改操作 將a x 的值加上c 求和操作 求此時a l.r 的和。這是最容易的模型,不需要任何輔助陣列。...

樹狀陣列的三種模式

我們在這之前已經了解過了lowbit的概念,還有對於樹狀陣列的基本的認識了,然後,再學習樹狀陣列的基礎用法。樹狀陣列主要有三種型別,從簡單到複雜。依次是修改點,求區間和 修改區間求點,還有最複雜的修改區間,求區間。最簡單的第一種模型 int lowbit int x void add int x,i...