NYOJ 108 士兵殺敵1(樹狀陣列)

2022-07-05 04:00:16 字數 1911 閱讀 5620

首先,要先講講樹狀陣列:

樹狀陣列(binary indexed tree(bit), fenwick tree)是乙個查詢和修改複雜度都為log(n)的資料結構。主要用於查詢任意兩位之間的所有元素之和,但是每次只能修改乙個元素的值;經過簡單修改可以在log(n)的複雜度下進行範圍修改,但是這時只能查詢其中乙個元素的值。

來觀察上面的圖:

令這棵樹的結點編號為c1,c2...cn。令每個結點的值為這棵樹的值的總和,那麼容易發現:

c1 = a1

c2 = a1 + a2

c3 = a3

c4 = a1 + a2 + a3 + a4

c5 = a5

c6 = a5 + a6

c7 = a7

c8 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8

...c16 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16

這裡有乙個有趣的性質:

設節點編號為x,那麼這個節點管轄的區間為2^k(其中k為x二進位制末尾0的個數)個元素。因為這個區間最後乙個元素必然為ax,

所以很明顯:cn = a(n – 2^k + 1) + ... + an

算這個2^k有乙個快捷的辦法,定義乙個函式如下即可:

1

int lowbit(intx)2

當想要查詢乙個sum(n)(求a[n]的和),可以依據如下演算法即可:

step1:

令sum = 0,轉第二步;

step2:

假如n <= 0,演算法結束,返回sum值,否則sum = sum + cn,轉第三步;

step3:

令n = n – lowbit(n),轉第二步。

1

int sum(intn)2

9return

sum;

10 }

可以看出,這個演算法就是將這乙個個區間的和全部加起來,為什麼是效率是log(n)的呢?以下給出證明:

n = n – lowbit(n)這一步實際上等價於將n的二進位制的最後乙個1減去。而n的二進位制裡最多有log(n)個1,所以查詢效率是log(n)的。

那麼修改呢,修改乙個節點,必須修改其所有祖先,最壞情況下為修改第乙個元素,最多有log(n)的祖先。

所以修改演算法如下(給某個結點i加上x):

step1:

當i > n時,演算法結束,否則轉第二步;

step2:

ci = ci + x, i = i + lowbit(i)轉第一步。

i = i +lowbit(i)這個過程實際上也只是乙個把末尾1補為0的過程。

**如下: 

1

void change(int i,intx)2

8 }

對於陣列求和來說樹狀陣列簡直太快了!

好了,因此士兵殺敵1的題目**可用樹狀陣列求和:

**出處

1 #include"

stdio.h

"2 #include

3int a[1000000];4

intmain()520

}21for(i=0;i)

2231

while(j>=1)32

36 printf("

%d",s2-s1);

37 putchar('\n'

);38}39

return0;

40 }

nyoj 108 士兵殺敵(一)

時間限制 1000 ms 記憶體限制 65535 kb 難度 3 描述 南將軍手下有n個士兵,分別編號1到n,這些士兵的殺敵數都是已知的。小工是南將軍手下的軍師,南將軍現在想知道第m號到第n號士兵的總殺敵數,請你幫助小工來回答南將軍吧。注意,南將軍可能會問很多次問題。輸入 只有一組測試資料 第一行是...

NYOJ 108 士兵殺敵(一)

時間限制 1000 ms 記憶體限制 65535 kb難度 3 描述 南將軍手下有n個士兵,分別編號1到n,這些士兵的殺敵數都是已知的。小工是南將軍手下的軍師,南將軍現在想知道第m號到第n號士兵的總殺敵數,請你幫助小工來回答南將軍吧。注意,南將軍可能會問很多次問題。輸入 只有一組測試資料 第一行是兩...

NYOJ 108士兵殺敵(一)

時間限制 1000 ms 記憶體限制 65535 kb 難度 3 描述 南將軍手下有n個士兵,分別編號1到n,這些士兵的殺敵數都是已知的。小工是南將軍手下的軍師,南將軍現在想知道第m號到第n號士兵的總殺敵數,請你幫助小工來回答南將軍吧。注意,南將軍可能會問很多次問題。輸入只有一組測試資料 第一行是兩...