1166 敵兵布陣 樹狀陣列

2021-08-10 07:19:06 字數 3296 閱讀 4668

網路上面都有這個圖,但是我將這個圖做了2點改進。

(1)圖中有一棵滿二叉樹,滿二叉樹的每乙個結點對應a中的乙個元素。

(2)c[i]為a[i]對應的那一列的最高的節點。

現在告訴你:序列c就是樹狀陣列。

那麼c如何求得?

c[1]=a[1];

c[2]=a[1]+a[2];

c[3]=a[3];

c[4]=a[1]+a[2]+a[3]+a[4];

c[5]=a[5];

c[6]=a[5]+a[6];

c[7]=a[7];

c[8]= a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];

以上只是列舉了所有的情況,那麼推廣到一般情況,得到乙個c[i]的抽象定義:

因為a中的每個元素對應滿二叉樹的每個葉子,所以我們乾脆把a中的每個元素當成葉子,那麼:c[i]=c[i]的所有葉子的和。

現在不得不引出關於二進位制的乙個規律:

先仔細看下圖:

將十進位制化成二進位制,然後觀察這些二進位制數最右邊1的位置:

1 --> 00000001

2 --> 00000010

3 --> 00000011

4 --> 00000100

5 --> 00000101

6 --> 00000110

7 --> 00000111

8 --> 00001000

1的位置其實從我畫的滿二叉樹中就可以看出來。但是這與c有什麼關係呢?

接下來的這部分內容很重要:

在滿二叉樹中,

以1結尾的那些結點(c[1],c[3],c[5],c[7]),其葉子數有1個,所以這些結點c[i]代表區間範圍為1的元素和;

以10結尾的那些結點(c[2],c[6]),其葉子數為2個,所以這些結點c[i]代表區間範圍為2的元素和;

以100結尾的那些結點(c[4]),其葉子數為4個,所以這些結點c[i]代表區間範圍為4的元素和;

以1000結尾的那些結點(c[8]),其葉子數為8個,所以這些結點c[i]代表區間範圍為8的元素和。

擴充套件到一般情況:

i的二進位制中的從右往左數有連續的x個「0」,那麼擁有2^x個葉子,為序列a中的第i-2^x+1到第i個元素的和。

終於,我們得到了乙個c[i]的具體定義:

c[i]=a[i-2^x+1]+…+a[i],其中x為i的二進位制中的從右往左數有連續「0」的個數。

理解了c[i]後,前i個元素的和s[i]就很容易實現。

從c[i]的定義出發:

c[i]=a[i-2^x+1]+…+a[i],其中x為i的二進位制中的從右往左數有連續「0」的個數。

我們可以知道:c[i]是肯定包括a[i]的,那麼:

s[i]=c[i]+c[i-2^x]+…

也許上面這個公式太抽象了,因為有省略號,我們拿乙個具體的例項來看:

s[7]=c[7]+c[6]+c[4]

因為c[7]=a[7],c[6]=a[6]+a[5],c[4]=a[4]+a[3]+a[2]+a[1],所以s[7]=c[7]+c[6]+c[4]

現在直接告訴你結論:2^x=i&(-i)

證明:設a』為a的二進位制反碼,i的二進位制表示成a1b,其中a不管,b為全0序列。那麼-i=a』0b』+1。由於b為全0序列,那麼b』就是全1序列,所以-i=a』1b,所以:

i&(-i)= a1b& a』1b=1b,即2^x的值。

x 為 i 的二進位制的最後一位1之後0的個數

假如 i=7 ,二進位制0000 0111 ,那麼x=0,s[7]=c[7]+c[7-2^0]+...

i=6             0000 0110          x=1   s[7]=c[7]+c[7-2^0]

+c[6-2^1]

i=4             0000 0100          x=2   

s[7]=c[7]+c[7-2^0]

+c[6-2^1]+c[4-2^2]

s=c[7]+c[6]+c[4];

i -= 4-2^2;  //不滿足i>0的條件退出迴圈

int sum(int i) //返回前i個元素和

return s;

}正如第01講提到的小石塊問題,如果陣列a[i]被更新了怎麼辦?那麼如何改動c?

如果改動c也需要o(n)的時間複雜度,那麼樹狀陣列就沒有任何優勢。所以樹狀陣列在改動c上面的時間效率為o(logn),為什麼呢?

因為改動a[i]只需要改動部分的c。這一點從第02講的圖中就可以看出來:

如上圖:

假如a[3]=3,接著a[3]+=1,那麼哪些c需要改變呢?

答案從圖中就可以得出:c[3],c[4],c[8]。因為這些值和a[3]是有聯絡的,他們用樹的關係描述就是:c[3],c[4],c[8]是a[3]的祖先。

那麼怎麼知道那些c需要變化呢?

我們來看「a」這個結點。這個「a」結點非常的重要,因為他體現了乙個關係:a的葉子數為c[3]的2倍。因為「a」的左子樹和右子樹的葉子數是相同的。 因為2^x代表的就是葉子數,所以c[3]的父親是a,a的父親是c[i+2^0],即c[3]改變,那麼c[3+2^0]也改變。

我們再來看看「b」這個結點。b結點的葉子數為2倍的c[6]的葉子數。所以b和c[6+2^1]在同一列,所以c[6]改變,c[6+2^1]也改變。

推廣到一般情況就是:

如果a[i]發生改變,那麼c[i]發生改變,c[i]的父親c[i+2^x]也發生改變。

這一行的迭代過程,我們可以寫出當a[i]發生改變時,c的更新函式為:

void update(int i,int value)  //a[i]的改變值為value

}#include

using namespace std;

int a[10005];

int n;

int lowbit(int t)

void insert(int t, int d)

}int getsum(int t)

return sum;

}int main()

char str[10];

scanf("%s", str);

printf("case %d:\n", ++t);

while(strcmp(str, "end") != 0)

else if(strcmp(str, "add") == 0)

else if(strcmp(str, "sub") == 0)

scanf("%s", str);}}

return 0;

}

HDU 1166 敵兵布陣 樹狀陣列

用樹狀陣列很簡單,太晚了,貼下 睡覺去。另,研究線段樹的時候,發現網上流傳著有幾種不同的線段樹,最正宗的是以單位區間為單位,只能處理線段 另外還有幾種葉子結點是點的,這種也可以用來處理點,所以這題是可以用這種線段樹做的。還搞不太清楚它們之間的關係。mark一下,明天再說。include includ...

HDU 1166 敵兵布陣 樹狀陣列

problem description c國的死對頭a國這段時間正在進行軍事演習,所以c國間諜頭子derek和他手下tidy又開始忙乎了。a國在海岸線沿直線布置了n個工兵營地,derek和tidy的任務就是要監視這些工兵營地的活動情況。由於採取了某種先進的監測手段,所以每個工兵營地的人數c國都掌握的...

HDU 1166 敵兵布陣 (樹狀陣列)

敵兵布陣 time limit 1000msmemory limit 32768kb64bit io format i64d i64u submit status description c國的死對頭a國這段時間正在進行軍事演習,所以c國間諜頭子derek和他手下tidy又開始忙乎了。a國在海岸線沿...