《原神攻略》幻影心流第三日打法思路
樹狀陣列
講樹狀陣列前需要有個大前提----lowbit()函式
lowbit(x)是x的二進位制表示式中最低位的1所對應的值
就比如說,6的二進位制是110,所以lowbit(6)=2
在學樹狀陣列前,我們要學會lowbit()函式常用程式碼寫法:
下面我們學習用lowbit(x)來維護區間
大前提設節點編號為x,那麼該結點維護的區間和是( x-lowbit(x),x ]
假設二叉樹有編號有x={1、2、3、4、5、6、7、8},共8個結點//藉助二叉樹來分析
用陣列A[1]、A[2]......A[8]儲存,那麼各個結點維護區間和 的區間(c[i])為:
c1維護區間(1-lowbit(1),1]即(0,1]A1
c2維護區間(2-lowbit(2),2]即(0,2]A1+A2
同理:c3維護(2,3]A3
c4維護(0,4]A1+A2+A3+A4
c5維護(4,5]A5
c6維護(4,6]A5+A6
c7維護(6,7]A7
c8維護(0,8]A1+A2+A3+A4+A5+A6+A7+A8
我們設C[i]代表子樹的葉結點權值之和:
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]陣列的結點序號轉化為二進位制 1=(001) C[1]=A[1];
2=(010) C[2]=A[1]+A[2];
3=(011) C[3]=A[3];
4=(100) C[4]=A[1]+A[2]+A[3]+A[4];
5=(101) C[5]=A[5];
6=(110) C[6]=A[5]+A[6];
7=(111) C[7]=A[7];
8=(1000) C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];
對照式子可以發現 C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; //區間和表示 (k為i的二進位制中從最低位到高位連續零的長度)
如驗證i=6,k=1。C[6]=A[6-2^1+1]+A[6-2^1+2]+..A[6] = A[5]+A[6]
lowbit(x) 就是取出x的最低位1,換言之lowbit(x)=2^k
那麼我們可以得到一個重要結論:
C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i];
C[i]=A[i-lowbit(i)+1]+A[i-lowbit(i)+2]+......A[i];
之後我們結合二叉樹性質來分析:
A[8]={1,2,0,1,3,0,2,1} //假定初值
C[i]表示儲存的區間和(附上一張圖)
由此我們可以推出四條重要性質:(最好結合圖例,將其背過)
(性質1)每個內部結點C[x]儲存以它為根的子樹中所有葉結點的和。
(性質2)每個內部結點C[x]的子結點個數等於lowbit(x)的值。
(性質3)除樹根以外,每個內部結點C[x]的父親結點是C[x+lowbit(x)]
(性質4)樹的深度為O(logN)
緊接著我們再來分析一下它的性質:
(性質1)每個內部結點C[x]儲存以它為根的子樹中所有葉結點的和。
這裡就會運用到樹狀陣列中一個重要的演算法:查詢字首和(這裡附上程式碼)
****當然,他還是有兩個大前提的:
大前提(1) . lowbit(x)運算(上文已講述)
大前提(2) . 樹狀陣列處理的下標為1..n的陣列,絕不能出現下標0的情況。因為lowbit(0)=0 陷入死迴圈
樣例:x=5,求C[5] //即C[5]字首和c1~c5之和
1迴圈: while(5>0) res=c[5]=a[5]=3 x=5-lowbit(5)=5-1=4 2迴圈: while(4>0) res=c[5]+c[4]=3+4=7 x=4-lowbit(4)=4-4=0 //迴圈結束,返回res=7
(性質3)除樹根以外,每個內部結點C[x]的父親結點是C[x+lowbit(x)]
這裡就會運用到另一個演算法:單點修改
樣例:由上圖n=8,x=5,y=10使a[5]+10區間修改過程:
迴圈1. while(5<=8) c[5]=3+10=13 x=5+lowbit(5)=5+1=6 //父編號
迴圈2. while(6<=8) c[6]=3+10=13 x=6+lowbit(6)=6+2=8 //父編號
迴圈3. while(8<=8) c[8]=10+10=20 x=8+lowbit(8)=16>n //迴圈結束
最後還剩一個區間和計算:sum(y)-sum(x-1)(此處不再多講)
完美撒花!