首先,要先講講樹狀陣列:
樹狀陣列(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當想要查詢乙個sum(n)(求a[n]的和),可以依據如下演算法即可:int lowbit(intx)2
step1:
令sum = 0,轉第二步;
step2:
假如n <= 0,演算法結束,返回sum值,否則sum = sum + cn,轉第三步;
step3:
令n = n – lowbit(n),轉第二步。
1可以看出,這個演算法就是將這乙個個區間的和全部加起來,為什麼是效率是log(n)的呢?以下給出證明:int sum(intn)2
9return
sum;
10 }
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號士兵的總殺敵數,請你幫助小工來回答南將軍吧。注意,南將軍可能會問很多次問題。輸入只有一組測試資料 第一行是兩...