一直覺得樹狀數組能用線段樹水過去,直到我今天碰上了樹狀數組模板題。html
而後就是開始認真的學習樹狀數組,忽然發現怎麼這麼好寫qwqqqq。ios
部分圖片轉自http://www.javashuo.com/article/p-aiipxaiq-kd.html數組
一.樹狀數組數據結構
樹狀數組是一種數據結構,核心思想是利用二進制的補碼思想。ide
首先就是樹狀數組的結構圖函數
而後咱們對他進行變形學習
是否是感受更好理解了呢?spa
而後咱們對其進行標號3d
c數組表示的是記錄的值,A數組表示的是原序列。而後就是全部關於樹狀數組的博客都會有的c數組的存值的展現,博主較懶就不寫了,具體看推薦博客。code
最重要的性質是
C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; (k爲i的二進制中從最低位到高位連續零的長度)
上面說k是二進制中最低位到最高位的連續零的長度也就是6的二進制是110,那麼k就是1,而後咱們帶入:
c[6]=A[6-2+1]+A[6-2+2]=A[5]+A[6]
看,是否是與上方式子相符,這就是lowbit要實現的功能。那麼,lowbit實現的原理是啥呢?
先粘一下lowbit函數的代碼
1 int lowbit(int k) 2 { 3 return k&(-k); 4 }
k&(-k)是啥意思呢?
-k是k的補碼,也就是反碼+1,反碼是啥自行百度,反正也很簡單。
而後你就會發現,你可以取出最小一位的1,而在上面的例子中就是2,你能夠再用幾組數據試一下,發現lowbit(i)=2^i。
4(0100),反碼4(0011),補碼(0100),0100&0100=0100,則lowbit(i)=4,又根據連續0的位數,則k=2,2^2=4,則假設成立。
因此,是否是很是的明白了?
其實還有一種lowbit的寫法,k&(k^(k-1)),能夠本身手推一下。
而後就是單點修改,代碼
1 void add(int x,int val) 2 { 3 while(x<=n) 4 { 5 tree[x]+=val; 6 x+=lowbit(x); 7 } 8 }
爲啥是i<=n呢?由於你單點修改是要從葉子結點蹦向父節點的,因此要從小到大。
1 int sum(int x) 2 { 3 int ans=0; 4 while(x>0) 5 { 6 ans+=tree[x]; 7 x-=lowbit(x); 8 } 9 return ans; 10 }
那爲啥區間查詢是i!=0呢?由於區間查詢是從父節點蹦向子節點的,因此i要不斷減少,記住區間查詢是查詢的前綴和,因此要查[l,r]的話須要sum(r)-sum(l-1)。
會了以上操做,就先作一下模板題吧 P3374 【模板】樹狀數組 1、
若是咱們想區間修改呢?你該不會是枚舉而後add(i)吧,不T纔怪啊!
這時咱們就用到了一種新的思想,差分思想。差分思想是很常見的,下面介紹一下:
數組a[]={1,6,8,5,10},那麼差分數組b[]={1,5,2,-3,5}
也就是說b[i]=a[i]-a[i-1];(a[0]=0;),那麼a[i]=b[1]+....+b[i];(這個很好證的)。
假如區間[2,4]都加上2的話
a數組變爲a[]={1,8,10,7,10},b數組變爲b={1,7,2,-3,3};
發現了沒有,b數組只有b[2]和b[5]變了,由於區間[2,4]是同時加上2的,因此在區間內b[i]-b[i-1]是不變的.
因此對區間[x,y]進行修改,只用修改b[x]與b[y+1]:
b[x]=b[x]+k;b[y+1]=b[y+1]-k;
因此,咱們在存的時候就存差分數組,而後咱們只改變l,與r+1的值,就行了。
那麼單點查詢呢?別忘了樹狀數組存的是前綴和!咱們直接sum(a)。
那差分下的區間查詢呢?直接原數列a[r]-a[l-1]!
二.二維樹狀數組
二維樹狀數組,就是矩陣嘛!你按照矩陣的方式作不就行了!
在add和sum函數裏,兩層循環,一層y一層x,不就行了!
而後就是二位樹狀數組全部的代碼演示
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; int a[2000][2000],n; int lowbit(int k) { return k&(-k); } void change(int i,int y) { int j; while(i<=n) { j=y; while(j<=n) { a[i][j]++; j+=lowbit(j); } i+=lowbit(i); } } int ask(int i,int y) { int ans=0; int j; while(i!=0) { j=y; while(j!=0) { ans+=a[i][j]; j-=lowbit(j); } i-=lowbit(i); } return ans; } int main() { int T; scanf("%d",&T); while(T--) { memset(a,0,sizeof(a)); int m; scanf("%d%d",&n,&m); while(m--) { char opt; cin >> opt; if(opt=='C') { int x1,x2,y1,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); change(x2+1,y2+1); change(x1,y1); change(x1,y2+1); change(x2+1,y1); } else { int x,y; scanf("%d%d",&x,&y); printf("%d\n",ask(x,y)&1); } } printf("\n"); } }