此為演算法速成blog,因此會略去一些推導細節。
樹狀陣列是乙個**查詢和插入時間複雜度都在o(logn)**的資料結構。
主要用於查詢區間和或者前n項和,但**每次只能修改乙個元素的值。**經過簡單修改可以在log(n)的複雜度下進行範圍修改,但是這時只能查詢其中乙個元素的值(如果加入多個輔助陣列則可以實現區間修改與區間查詢)。
來觀察這個圖:
令這棵樹的結點編號為c1,c2…cn. ci的值為子樹葉子結點的權值總和,那麼容易發現:
c1 = a1
c2 = a1 + a2
c3 = a3
c4 = a1 + a2 + a3 + a4
c5 = a5
c6 = a5 + a6
c7 = a7
c8 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8
…
答案是,這樣會使操作更簡單!看到這相信有些人就有些感覺了,為什麼複雜度被log了呢?可以看到,c8可以看作a1~a8的左半邊和+右半邊和,而其中左半邊和是確定的c4,右半邊其實也是同樣的規則把a5~a8一分為二……繼續下去都是一分為二直到不能分樹狀陣列巧妙地利用了二分,樹狀陣列並不神秘,關鍵是巧妙!那麼現在,我們來通過a求c.
這裡給出一條公式: c[i]=a[i-2k+1]+a[i-2k+2]+…a[i] k為i二進位制位的末尾0長度, i >= 1。
這個公式什麼意思呢?它其實是說明了c[i]的管轄範圍在[i-2k+1 , i ]。
比如說,i = 8,二進位制為1000,k = 3,2k= 8,那麼c[8]就是a[1]…a[8]的權值總和。
i = 7,二進位制為111,k = 0, 2k = 0,那麼c[7]就是a[7].
下面給出計算2k的方法。
int
lowbit
(int x)
至於為什麼是這樣,我現在也不太清楚。大家可以檢視其他的blog. 我們現在是速成,這個公式直接記住就可以。
有了上面的函式,我們的公式可以改寫成c[i] = a[i-lowbit(i)+1] + a[i-lowbit(i)+2] +…+a[i];
也就是c[i]的管轄範圍在[ i-lowbit(i)-1, i ].
ok,現在你已經知道c[i]怎麼算了。那麼回到應用,如何利用c[i]計算區間和或者前n項和呢?下面先給出**。
int
sum(
int k)
//前k個數的和
return ans;
}
拿k = 6舉例子,c4 = a1 + a2 + a3 + a4, c6 = a5 + a6,那麼sum = c4 + c6;
i = 6,lowbit = 2;i = 4,lowbit = 4;
因此可以發現,傳進去的引數k表示從1到k的整個區域,lowbit(i)表示c[i]的管轄範圍,實際上是將一段段的c全部加起來。
那麼如何求區間和呢?下面直接上**。
int
ask(
int l,
int r)
//求l ~ r區間和
其實就是後面的前n項和與前面的前n項和相減,就可以得到中間的區間和了嘛。
那麼當改變乙個a[i]的值之後,怎麼更新c[i]呢?
void
change
(int x,
int p)
//將第x個數加p
return
;}
從第x個數開始,當前的c[x]加上p,然後跳到下乙個與a[x]有關的c (x + lowbit(x)),也加上p. 實際上就是將範圍擴大,把與a[x]有關的c全部都加上p.
總**:
#include
#define max 10010
using namespace std;
int t[max]
, a[max]
;int n;
intlowbit
(int x)
//求c管轄範圍 2^k
intsum
(int k)
//前k個數的和
return ans;
}int
ask(
int l,
int r)
//求l ~ r區間和
void
change
(int x,
int p)
//將第x個數加p
return;}
intmain()
int l ,r;
while
(cin>>l>>r)
return0;
}
演算法 樹狀陣列
演算法適用於求前k個整數的和,一般的做法是設定乙個sum陣列來表示 1 問題公升級一下 在查詢的過程中隨時給第x個整數加上乙個整數v,同樣要求查詢第k個整數的和。此時若還是原來的sum陣列,則要遍歷來更新,複雜度為o n 而用樹狀陣列則時間要更快。2 再來一題 很簡單的,別怕昂 計算序列中在元素左邊...
演算法 樹狀陣列
相信大家都會樹狀陣列的 單點修改,區間查詢 或者 區間修改,單點查詢 博主就不細講了。但是博主今天發現了乙個神奇的演算法 博主太菜 它可以使用樹狀陣列維護 區間修改,區間查詢 就是乙個優化過的字首和,使查詢和修改協調為 theta log 2n namespace fentree void add ...
演算法 樹狀陣列
要學樹狀陣列的先看懂一幅圖 這就是樹狀陣列的儲存方式。那麼樹狀陣列的優點是什麼呢,允許任意修改,可快速提取出a陣列內數字 據圖可知 c1 a1,c2 a1 a2,c3 a3,c4 a1 a2 a3 a4,c5 a5,c6 a5 a6,c7 a7,c8 a1 a2 a3 a4 a5 a6 a7 a8,...