NYOJ 116士兵殺敵 二 樹狀陣列

2022-05-24 10:09:09 字數 1756 閱讀 6039

士兵殺敵(一) 陣列是固定的,所以可以用乙個sum陣列來儲存每個元素的和就行,但是不能每次都加,因為那樣會超時,查詢次數太多。但是這個士兵殺敵(二)就不能用那個方法來解了,因為這個是動態的,中間元素的值可能會變化,所以引出乙個新的東西來。剛開始想了一下,實在是沒有想到方法,就去討論區看了看,一看好像都說用樹狀陣列,就去找樹狀陣列的用法。

先上圖,看著**釋容易理解點。

陣列a是原陣列中的元素,陣列c是樹狀陣列中的元素,圖中c陣列的元素組成為a中的某些元素之和,這些元素的個數取決於它的下標能被多少個2整除,像c[1] = a[1]; c[2] = a[1] + a[2]; c[3] = a[3]; c[4] = a[1] + a[2] + a[3] + [4] = c[2] + c[3]; ……這些個數可以寫乙個通式c[i] = a[n - 2^k + 1] + ……+a[i]; 其中k為 i 的二進位制中從右往左數的 0 的個數 ,就像6有乙個, 6可以寫成 2 × 3, 所以c[6] = a[5] + a[6]; 所以可以定義乙個函式來求這個數.

6的二進位制為0110

5的二進位制為0101

6^5 = 0011

6&(6^5) = 0010 = 十進位制中的2

所以函式可以這麼寫

int lowbit(int n)//

求n中有多少個能被2的多少次冪整除的,即2^k, 也就是樹狀陣列的作用域

也可以寫成

int lowbit(int n)//

求n中有多少個能被2的多少次冪整除的,即2^k, 也就是樹狀陣列的作用域

更改乙個數的值, 就要更改次數在樹狀陣列中的所有祖先,不過這個時間複雜度是o(logn); 下面是更改值(新增殺敵數)的函式

void add(int pos, int num)//

新增新值到樹狀陣列中

}

下面就是求和函式, 因為這種方法之所以快,是求他的最小樹根節點的和, 最小樹的個數為當前要求的n的二進位制中為1的個數,即展開式中能寫成不同2的冪指數的項數,

例如: 15 = 2^3 + 2^2 + 2^1 + 2^0; 所以n = 15時, 最小數有四個,求和的時間複雜度為o(logn); 

int sum(int n)//

求前n個數的和

return

sum;

}

關鍵就是這三步, 這三步搞明白了,基本上就不成問題了,但是,當時按照 殺敵(一) 中的思維,還統計了乙個總數,那樣不會快,反而會慢,所以直接求就行,下面是完整的**

1 #include 2 #include 34

int tmp[1001000];5

intn, k;67

int lowbit(int n)//

求n中有多少個能被2的多少次冪整除的,即2^k, 也就是樹狀陣列的作用域811

12void add(int pos, int num)//

新增新值到樹狀陣列中

1319}20

21int sum(int n)//

求前n個數的和

2229

return

sum;30}

3132

intmain()

3342

for(int i = 0; i < k; i++)

4350

51return0;

52 }

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

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

(樹狀陣列)NYOJ116 士兵殺敵(二)

傳送門 nyoj116 士兵殺敵 二 描述 南將軍手下有n個士兵,分別編號1到n,這些士兵的殺敵數都是已知的。小工是南將軍手下的軍師,南將軍經常想知道第m號到第n號士兵的總殺敵數,請你幫助小工來回答南將軍吧。南將軍的某次詢問之後士兵i可能又殺敵q人,之後南將軍再詢問的時候,需要考慮到新增的殺敵數。輸...

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

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