樹狀數組及二維樹狀數組
一直以為樹狀數組能用線段樹水過去,直到我今天碰上了樹狀數組模板題。
然後就是開始認真的學習樹狀數組,突然發現怎麽這麽好寫qwqqqq。
部分內容轉自https://www.cnblogs.com/hsd-/p/6139376.html
一.樹狀數組
樹狀數組是一種數據結構,核心思想是利用二進制的補碼思想。
首先就是樹狀數組的結構圖
然後我們對他進行變形
是不是感覺更好理解了呢?
然後我們對其進行標號
c數組表示的是記錄的值,A數組表示的是原序列。然後就是所有關於樹狀數組的博客都會有的c數組的存值的展示,博主較懶就不寫了,具體看推薦博客。
最重要的性質是
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 }lowbit
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"); } }二維樹狀數組
樹狀數組及二維樹狀數組