樹狀陣列點更新,區間更新理解

2021-07-23 08:57:37 字數 2348 閱讀 3299

對於乙個數列a1a2a3…an,要求支援兩種操作:

1.查詢[x,y]區間的區間和

2.把[x,y]區間每個元素加val

事實上線段樹也可以解決這樣的問題,用上一點lazy的思想,每次只更新小區間的區間和,查詢的時候加上祖先節點的影響就可以了。

我們用樹狀陣列也可以解決這樣的問題,並且效率會更高一些,複雜度都是nlog(n),但樹狀陣列的常數更小,空間占用也更少。

首先回憶一下樹狀陣列維護字首和的過程,首先對於任意乙個整數x,比如11,表達成二進位制為1011,而1011 = 1000 + 10 + 1,這樣的劃分不超過log(11)次,考慮讓乙個陣列c維護一小段區間和,讓c[11]維護以a[11]作為結尾,長度為1的區間(這個時候只有a[11]乙個值),讓c[11-1]維護以a[11-1]作為結尾,長度為10(2進製)的區間和,同理讓c[11-1-2]維護以a[11-1-2]作為結尾,長度為1000(2進製)的區間和,這樣我們的小區間就覆蓋了整個[1,11]的區間,字首和就可以累加得到了,基於這樣的思想我們設定了lowbit函式,他是這樣的:

int lowbit(int x)
這個函式利用位運算技巧返回了整數x的最低位的1和後續的0組成的整數,比如10 = 1010(2進製),那麼lowbit(10) = 2 = 10(2進製),根據上面的討論,這個函式實際上用來分解整數達到劃分區間的目的。

因此我們用c[i]維護a[i-lowbit(i)+1]a[i-lowbit(i)+2]…a[i]的區間和

假設c陣列已經構造好了,那麼我們查詢從[1,x]區間的字首和函式是這樣的:

int query(int x) 

return res;

}

聯絡上面11的例子,11 = 1000 + 10 + 1,所以c[11]只用管a[11]就可以了,剩餘的10個不管,c[10]只用管a[9],a[10]就可以了,c[8]管剩下所有的。

當某個a[i]加上乙個值val,如何更新呢

void update(int x, int

val)

}

首先可以明確的是,x + lowbit(x)必定會使x的二進位制數發生進製,而且是最小的進製,事實上很容易發現進製之後的數一定會管到原來的a[i],畫出樹狀圖就可以發現

c陣列也可以遞推求得,就不介紹了,**如下,很好懂

memset(c, 0, sizeof(c));

for(int i = 1; i <= n; i++)

對一段區間每個值加上乙個val如何處理呢?

假設存在乙個陣列add,add[x] = val 表示把[x,n]這個區間每個元素+val.

這樣當我們把[x,y]區間每個元素都+val的時候,把問題轉化為把[x,n]的區間每個元素+val,把[y+1,n]每個元素-val

這樣當我們查詢[1,x]區間的區間和時,實際的

sum[x] = a[1] + a[2] +… + a[x] + add[1] * (x+1-1) + add[2] * (x+1-2) + … + add[x] * (x+1-x) = (a[1] + a[2] + … + a[x]) + (x+1)(add[1] + add[2] + …+ add[x] ) - (1 * add[1] + 2*add[2] + …+x *add[x])

做到這裡問題就轉化為了求字首和了,第一項是a[x]的字首和直接維護即可,第二項和第三項分別用樹狀陣列維護即可。**很容易看懂。結合題目poj3468,以下是ac**

#include 

#include

#include

#include

using

namespace

std;

const

int maxn = 100005;

typedef

long

long ll;

int n,q,a[maxn];

ll sum[maxn];

ll c1[maxn],c2[maxn]; //c1維護add[i]的字首和,c2維護add[i]*i的字首和

int lowbit(int x)

void update1(int x,int val)

}ll query1(int x)

return res;

}void update2(int x,int val)

}ll query2(int x)

return res;

}int main()

else

if(cmd[0] == 'c')

}return

0;}

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

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

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

為了更好地使用複雜度比線段樹更加優化的樹狀陣列,所以必須實現樹狀陣列的區間更新 樹狀陣列時間複雜度為o mlogn 實際用的時候優於線段樹,且寫得少。引入差分陣列,假設初始資料陣列為a,另a 0 0 設要維護的差分陣列為 d i a i a i 1 進一步可知 a i d 1 d 2 d i 即前i...

5, 樹狀陣列的區間更新 區間查詢

經典的樹狀陣列的區間更新問題,和區間查詢問題 但是為什麼這麼求就是結果講道理我是真沒看懂那個 人的解釋,但是我知道這是正確的 include include include include using namespace std const int max 1e6 5 long long a max...