樹狀陣列模板 洛谷P3368

2021-08-21 21:07:36 字數 2658 閱讀 2853

樹狀陣列比較適合於對於給定陣列求字首和(區間和),但是只能單點更新,不容易做區間更新,但是今天學會了可以利用差分的方法來做區間更新,可以很快實現單點查詢;

上模板~

首先是lowbit:

inline int lowbit(int k)
然後是單點更新:

inline void add(int x,long long k)
然後是求字首和:

inline long long sum(int x)
模板結束;

現在看一下如何進行區間更新和單點查詢:

這模板題名字就是樹狀陣列..幹嘛用線段樹..**還賊長..

這裡介紹樹狀陣列+差分思想,算是對下面大神的補充吧。

何為差分

現在我們有乙個從小到大的數列a

a 1 3 6 8 9

然後還有乙個差分陣列b

b 1 2 3 2 1

相信某些小夥伴已經看出端倪了..這裡b[i]=a[i]-a[i-1],我令a[0]=0,故b[1]=a[1]。

擁有了b陣列,我們就可以很簡單的求出bit中任意乙個數,只需bit[i]=sigma(k=1 to i) b[k](這個很好推吧..)

我覺得現在該有人說我zz了..何必不直接查詢a[i]而是找這麼麻煩乙個方法..這裡我們轉回正題!別忘了,題目要我們進行區間修改..

我們知道,樹狀陣列對於單點值的修改十分方便(不懂的去看樹狀陣列1),對於區間的修改就比較尷尬..而我們又不想敲死長的線段樹..怎麼辦呢,這時候差分就顯出優勢

還是上面的a和b,現在我們使區間[2,4]的所有數均+2,則a/b變為

a 1 5 8 10 9

b 1 4 3 2 -1

事實上,這裡只有b[2]和b[5]發生了變化,因為區間內元素均增加了同乙個值,所以b[3],b[4]是不會變化的。

這裡我們就有了第二個式子:對於區間[x,y]的修改(增加值為d)在b陣列內引起變化的只有 b[x]+=d,b[y+1]-=d。(這個也很好推的..)

這樣,我們就把樹狀陣列的軟肋用差分解決了。

所以可以用差分的方法來進行區間更新;

可以發現,我們其實只需要維護 delta,即b陣列,對於原陣列可以不用儲存,因為對於任意的a[i]值,我們都可以通過a[x]=sigma[i=1 to x](b[i])來算出

就是a[i]=sigma(k=1 to i) b[k];

在讀入的時候,我們可以直接把a[i]-a[i-1]讀入陣列,在

主函式如下:

int main() 

while(m--)

if(com==2){

cin>>x;

cout在讀入的時候,我們只需要add(i,x-y);

在進行單點更新的時候,我們只需要add(x,k);add(y+1,-k);

最後我們求出的sum其實就是a[i]的值;

真巧妙 hohoho~

先附上一段copy的大神部落格裡的理解;

然後我簡單聊一下我對樹狀陣列的理解; 

你看啊; 

比如我們要求14的字首和,我們是不是只要加上14 12 8 所在的柱就好了; 

我們分析分析; 

14-1110 

12-1100 

08-1000 

我們是不是加上14後刪掉14的最後乙個1,即lowbit就好啦; 

在來一組 

16-10000 

08-1000 

06-0110 

我們結合圖可以知道 

8的字首 包含了6的值; 

16的字首包含了8的值,也包含了6的值; 

如果6的值修改了;8的字首和和16字首和是不是也要改; 

所以我們要把8和16也改掉; 

所以我們加lowbit; 

對吧

所以我們可以看到,如果我們想要求乙個點x的字首和,我們只需要把所有與x字首和相關的點的值加起來,這些相關的值,就儲存在x-lowbit(x)裡面,而且不停地減下去,直到0為止;比如,我想求23的字首和,23的二進位制表示是10111,去掉最後一位的1,得到了10110,就是22,同時,23的lowbit就是1,23-1=22;那麼得到了22,22的二進位制表示是10110,去掉最後一位的1,得到了10100,就是20,同時,22的lowbit就是2,22-2=20;那麼得到了20,20的二進位制表示是10100,去掉最後一位的1,得到了10000,就是16,同時,20的lowbit就是4,20-4=16;那麼得到了16,16的二進位制表示是10000,去掉最後一位的1,得到了00000,就是0,同時,16的lowbit就是16,16-16=0;那麼此時就是0。

通過上面這一段計算,我們得到了,23->22->20->16->0,得到了這幾個數字,實際上,我們所要求的sum(23)=c[23]+c[22]+c[20]+c[16]+c[0];

另一方面,如果我們想去改變點6的值,那麼我們需要向上改變哪些點的值呢?

首先,對於6這個值,我們求出他的lowbit(6)=2,然後我們需要更新6+2=8這個點;然後lowbit(8)=8,所以我們要去更新8+8=16這個點;然後lowbit(16)=16,然後我們要去更新16+16=32這個點;然後依次往上~直到碰到上限n為止;

上述就是sum和add兩個操作的理解;

最後omg

洛谷 P3368 模板 樹狀陣列 2

題目描述 如題,已知乙個數列,你需要進行下面兩種操作 1.將某區間每乙個數數加上x 2.求出某乙個數的和 輸入格式 第一行包含兩個整數n m,分別表示該數列數字的個數和操作的總個數。第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。接下來m行每行包含3或4個整數,表示乙個操作,具...

洛谷 P3368 模板 樹狀陣列 2

目錄 給出兩種操作 乙個是對指定區間每個數進行加法,另個是求指定數當前的值 這題其實跟p3367是差不多的 但是需要用到差分 來介紹一下差分 設陣列a 那麼差分陣列b 也就是說b i a i a i 1 a 0 0 那麼a i b 1 b i 這個很好證的 假如區間 2,4 都加上2的話 a陣列變為...

洛谷 P3368 模板 樹狀陣列 2

如題,已知乙個數列,你需要進行下面兩種操作 1.將某區間每乙個數數加上x 2.求出某乙個數的和 輸入格式 第一行包含兩個整數n m,分別表示該數列數字的個數和操作的總個數。第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。接下來m行每行包含2或4個整數,表示乙個操作,具體如下 操...