標籤:規律 遍曆 src get span 首碼 png 維護 images
樹狀數組淺講
rt,個人膚淺理解,各位神犇請自動出門右轉
不同於傳統數組每個元素單獨存放,求和時遍曆相加,樹狀數組每個元素不單獨維護,而是被維護在一個包含其他元素的首碼和裡。宜先仔細揣摩後再行。
便體現了樹狀數組儲存資料的原理
相當於以下等式
說明:C[]是樹狀數組,A[]是實際元素
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];
如此儲存,便可以體現出上面所說每個元素不單獨維護,而是被維護在一個包含其他元素的首碼和裡的維護原則,比如元素1(即\(A[1]\))便包含在C[1],C[2],C[4],C[8]中,而如今有一個神器lowbit(x)=x&(-x)可以依次推出所有包含元素i的\(C[k]\)們,我們先看看1至8的lowbit值
lowbit(1)=1
lowbit(2)=2
lowbit(3)=1
lowbit(4)=4
lowbit(5)=1
lowbit(6)=2
lowbit(7)=1
lowbit(8)=8
這時你會發現一個規律:每次當前下標再加上當前下標的lowbit值便會得到下一個包含元素i的\(C[k]\)的下標,比如第一個包含元素1的\(C[k]\)為\(C[1]\),那麼下一個(第二個)包含元素1的\(C[k]\)為\(C[1+lowbit(1)]\),如此按照此規律迴圈便可訪問到所有包含元素i的\(C[k]\)們
於是下面遍曆所有包含元素i的\(C[k]\)的程式成立
void spot(int x){ for(int i=x;i<=n;i+=lowbit(i)) //do something}
於是你又可以在遍曆的同時順便修改一下值(又於是下面修改元素i的值的程式成立)
void add(int x, int v){ for(int i=x;i<=n;i+=lowbit(i)) tree[i]+=v;}
同理,你又會發現其實查詢首碼和的規律也是如此,讀者可以動手類比一下
於是查詢首碼和的程式又成立
int getsum(int x){ int ans=0; for(int i=x;i>0;i-=lowbit(i)) ans+=tree[i]; return ans;}
完整的樹狀數組板子
int lowbit(int x){return x&(-x);}void add(int x, int v){ for(int i=x;i<=n;i+=lowbit(i)) tree[i]+=v;}int getsum(int x){ int ans=0; for(int i=x;i>0;i-=lowbit(i)) ans+=tree[i]; return ans;}
以上。
樹狀數組淺講