NYOJ 士兵殺敵 (樹狀陣列)

2021-05-27 09:27:18 字數 2515 閱讀 2115

首先看一道題:

描述南將軍手下有n個士兵,分別編號1到n,這些士兵的殺敵數都是已知的。

小工是南將軍手下的軍師,南將軍經常想知道第m號到第n號士兵的總殺敵數,請你幫助小工來回答南將軍吧。

南將軍的某次詢問之後士兵i可能又殺敵q人,之後南將軍再詢問的時候,需要考慮到新增的殺敵數。

輸入

只有一組測試資料

第一行是兩個整數n,m,其中n表示士兵的個數(1

輸出對於每次查詢,輸出乙個整數r表示第m號士兵到第n號士兵的總殺敵數,每組輸出佔一行

樣例輸入

5 61 2 3 4 5

query 1 3

add 1 2

query 1 3

add 2 3

query 1 2

query 1 5

樣例輸出68

820問題提出:已知陣列a,元素個數為n,現在更改a中的元素,要求得新的a陣列中i到j區間內的和(1<=i<=j<=n).

思考:對於這個問題,我們可以暴力地來解決,從a[i]一直累加到a[j],最壞的情況下複雜度為o(n),對於m次change&querry,合起來的複雜度為o(m*n),在n或m很大的情況下,這樣的複雜度是讓人無法忍受的.另外,如果沒有元素的變更,我們完全可以儲存sum[1,k](k=1,2,……),然後對任意給定的查詢區間[i,j],都可以方便的用ans=sum[1,j]-sum[1,i-1],當然這只是沒有元素改變的情況下的比較優化的解法.那麼對於有元素變更的問題是否有更高效的方法呢?(廢話!沒有我還寫啥?!)可以想一下,每次更改的元素是比較少的,有時候甚至每次只改變乙個元素,但是在用暴力方法求區間和的時候,卻對區間內所有的元素都累加了一遍,這樣其實造成了許多無謂的運算.這時候也許會想到如果能把一些結果存起來會不會減少很多運算?答案是肯定的,但問題是怎麼存,存什麼?如果存任意區間的話,n比較大的時候不但記憶體吃不消,而且儲存的量太大,不易更改,反而得不償失;那麼也許可以考慮儲存特定的一些區間(比如說線段樹,其實現在討論的問題用線段樹完全可以解,以後再詳細寫線段樹).那麼現在重新回過頭來,看下這個問題,我們已經確定了要儲存一些特定區間sum的想法,接下來我們要解決的無非是兩個問題:1、減少更改元素後對這些區間裡的sum值的更改時間.2、減少查詢的時間.

好了廢話了這麼半天,無非是想讓自己以及看到的人明白為什麼要用樹狀陣列.

接下來正式入題.

首先我們可以借鑑元素不變更問題的優化方法,先得到前i-1項之和and前j項之和,以s[i]表示前i項之和,那麼sum[i,j]=s[j]-s[i-1].那麼現在的問題已經轉化為求前i項之和了.另外,我們已經確定要儲存一些特定區間的和,現在就要來揭示這些特定的區間究竟指什麼.

從圖中不難發現,c[k]儲存的實際上是從k開始向前數k的二進位制表示中右邊第乙個1所代表的數字個元素的和(這麼說可能有點拗口,令lowbit為k的二進位制表示中右邊第乙個1所代表的數字,然後c[k]裡存的就是從a[k]開始向前數lowbit個元素之和)這麼存有什麼好處呢?無論是樹狀陣列還是線段樹,都用到了分塊的思想,而樹狀陣列採用這樣的儲存結構我想最主要的還是這樣方便計算,我們可以用位運算輕鬆地算出lowbit.分析一下這樣做的複雜度:對於更改元素來說,如果第i個元素被修改了,因為我們最終還是要求和,所以可以直接在c陣列裡面進行相應的更改,如圖中的例子,假設更改的元素是a[2],那麼它影響到得c陣列中的元素只有c[2],c[4],c[8],我們只需一層一層往上修改就可以了,這個過程的最壞的複雜度也不過o(logn);對於查詢來說,如查詢s[k],只需查詢k的二進位制表示中1的個數次就能得到最終結果,比如查詢s[7],7的二進位制表示中有3個1,也就是要查詢3次,到底是不是呢,我們來看上圖,s[7]=c[7]+c[6]+c[4],可能你還不知道怎麼實現這個過程.

還以7為例,二進位制為0111,右邊第乙個1出現在第0位上,也就是說要從a[7]開始向前數1個元素(只有a[7]),即c[7];

然後將這個1捨掉,得到6,二進位制表示為0110,右邊第乙個1出現在第1位上,也就是說要從a[6]開始向前數2個元素(a[6],a[5]),即c[6];

然後捨掉用過的1,得到4,二進位制表示為0100,右邊第乙個1出現在第2位上,也就是說要從a[4]開始向前數4個元素(a[4],a[3],a[2],a[1]),即c[4].

**實現:

int lowbit(int x)//計算lowbit

void add(int i,int val)//將第i個元素更改為val

}int sum(int i)//求前i項和

return s;

}

#include #include#include#includeusing namespace std;

int n,m;

int a[1000005];

int c[1000005]=;

int lowbit(int x)

int sum(int i)

return s;

}void change(int x,int m)//士兵增加

int main()

int m,n;

while(m--)

else

change(m,n);

}}

nyoj 士兵殺敵(一)(樹狀陣列)

士兵殺敵 一 描述南將軍手下有n個士兵,分別編號1到n,這些士兵的殺敵數都是已知的。小工是南將軍手下的軍師,南將軍現在想知道第m號到第n號士兵的總殺敵數,請你幫助小工來回答南將軍吧。注意,南將軍可能會問很多次問題。輸入 只有一組測試資料 第一行是兩個整數n,m,其中n表示士兵的個數 1 輸出對於每乙...

士兵殺敵(樹狀陣列)

南將軍手下有n個士兵,分別編號1到n,這些士兵的殺敵數都是已知的。小工是南將軍手下的軍師,南將軍經常想知道第m號到第n號士兵的總殺敵數,請你幫助小工來回答南將軍吧。南將軍的某次詢問之後士兵i可能又殺敵q人,之後南將軍再詢問的時候,需要考慮到新增的殺敵數。輸入只有一組測試資料 第一行是兩個整數n,m,...

nyoj 116 士兵殺敵(二) 樹狀陣列

時間限制 1000 ms 記憶體限制 65535 kb 難度 5 描述 南將軍手下有n個士兵,分別編號1到n,這些士兵的殺敵數都是已知的。小工是南將軍手下的軍師,南將軍經常想知道第m號到第n號士兵的總殺敵數,請你幫助小工來回答南將軍吧。南將軍的某次詢問之後士兵i可能又殺敵q人,之後南將軍再詢問的時候...