樹狀陣列區間更新 區間查詢 單點查詢

2021-07-27 06:01:37 字數 2032 閱讀 7630

為了更好地使用複雜度比線段樹更加優化的樹狀陣列,所以必須實現樹狀陣列的區間更新;樹狀陣列時間複雜度為o(mlogn), 實際用的時候優於線段樹,且寫得少。

引入差分陣列,假設初始資料陣列為a,另a[0] = 0;設要維護的差分陣列為 d[i] = a[i] - a[i-1];進一步可知 a[i] = d[1] + d[2] + ... + d[i]; 即前i項和,為方便記為sigma(d, i);例如如下例子:

a:1 2 3 5 6 9

d:1 1 1 2 1 3

如果進行區間[2, 5]更新加2,則:

a:1 4 5 7 8 9

d:1 3

1 2 1 1

會發現當某個區間[x, y]內值發生了同等改變,但這個區間內的差值還是不變的,只有d[x]和d[y+1]的值發生了改變。

所以可以建立區間更新、單點查詢的樹狀陣列去維護d,例如如下例子:

d:1 1 1 2 1 3

區間[2, 5]更新加2,則:

// 在區間[x, y]增加k

updata(n, c, x, k);

updata(n, c, y+1, -k);

// 獲得第i點的值

pointvalue(c, i);

根據差分陣列:

sum[n]

= a[1] + a[2] + .. + a[n];

= sigma(d, 1) + sigma(d

, 2) + ... + sigma(d, n);

= n*d[1] + (n-1)*d[2] + ... + 2*d[n-1] + 1*d[n];

= n*(d[1] + d[2] +...+ d[n]) -(0*d[1] + 1*d[2] + ... + (n-1)*d[n]);

所以可以得到 sum[n] =n * sigma(d, n) 

- (0*d[1] + 1*d[2] + ... + (n-1)*d[n]);

令r[i] = (i-1) * d[i];

則 sum[n] = n * sigma(d, n) - sigma(r, n);

此時則需要構造兩個樹狀陣列去分別維護d和r的更新;例如如下例子:

a:  1 2 3 5 6 9

d:  1 1 1 2 1 3

c1:1 2 1 5 1 4

r:   0 1 2 6 4 15

c2:0 1 2 9 4 19

如果進行區間[2, 5]更新加2,則:

a:  1 4 5 7 8 9

d:  1 3 1 2 1 1

c1:1 4 1 7 1 2

r:   0 3 2 6 4 5

c2:0 3 2 11 4 9

**如下:

int lowbit(int k)

void update(int n, int *c1, int *c2, int i, int va)

}int sum(int *c1, int *c2, int i)

return res;

}//-------------------------------------

// 在區間[x, y]增加k

updata(n, c, x, k);

updata(n, c, y+1, -k);

// 獲得[x, y]的和

res = sum(c1, c2, y) - sum(c1, c2, x-1);

其它細節可參考:

樹狀陣列 單點更新 區間查詢

input 每組測試用例首先一行是2個正整數n和m n 100000,m 10000 其中,n表示士兵的數量,m表示有m個詢問。接下來一行是n個正整數,依次表示n位士兵cf的rating。其中,rating的取值範圍是小於等於5000。最後是m行的詢問,每行包含2個正整數a和b,表示要計算從第a個士...

樹狀陣列的單點更新,區間查詢。

基本的陣列陣列概念,樹狀陣列利用其特殊的位置可以用二進位制達到log級別的更新,如下圖 核心 int sum int i return s void add int i,int x lowbit的作用是求出最低位的那個1,為什麼這樣可以求出來呢,是因為計算機中是採用補碼的方式來儲存數值型資料,所以負...

樹狀陣列區間更新 單點查詢

設a陣列表示原來的區間 c i a i a i 1 這樣可以看出 a i sum c 1 c 2 c i 例如 a 1 3 4 2 6 8 c 1 2 1 2 4 2 樹狀陣列維護的是c陣列 當把a 3,5 每個數都加2時,我們看c陣列,由於c陣列維護的是相鄰區間的差值,所以c 3 2 因為區間 3...