樹狀數組的本職工作是修改點,查詢區間和
我們可以先回顧一下姊妹篇:(一維)樹狀數組的實現
然后我們再回顧一下差分數組,差分數組可以實現修改區間,查詢點
如果不用樹狀數組進行優化的話,修改是O(1),查詢是O(n)的
我們要做的就是用樹狀數組把查詢操作優化成對數級別的
這里直接給出樹狀數組的代碼以及差分數組的代碼:
先是樹狀數組的:
int a[maxn]; int c[maxn]; int lowbit(int x) { return x&(-x); } void update(int x,int y) { while(x<=n) { c[x]+=y; x+=lowbit(x); } } int sum(int x) { int ans=0; while(x>0) { ans+=c[x]; x-=lowbit(x); } return ans; }
然后是差分數組的:
int a[maxn]; int b[maxn]; int n,q; void update(int x,int y,int z) { b[x]+=z; b[y+1]-=z; } int sum(int x) { int ans=0; for(int i=1;i<=x;i++) ans+=b[i]; return ans; }
那么我們只要把他們結合在一起,就是傳說中的差分樹狀數組了,其實很簡單,就是把差分數組存到樹狀數組里面來維護,但是這樣就會有一個問題,雖然查詢的效率變好了
但是修改的效率由原來的O(1)變成了O(logn)的,所以有利有弊需要自己衡量
下面直接給出差分樹狀數組的源碼,其實就是把上述的兩份代碼拼接在了一起
1 //aininot260 2 //修改區間,查詢點 3 #include<iostream> 4 #include<cstring> 5 using namespace std; 6 const int maxn=100005; 7 const int maxq=100005; 8 int a[maxn]; 9 int b[maxn]; 10 int c[maxn]; 11 int lowbit(int x) 12 { 13 return x&(-x); 14 } 15 int n,q; 16 void c_update(int x,int y) 17 { 18 while(x<=n) 19 { 20 c[x]+=y; 21 x+=lowbit(x); 22 } 23 } 24 void update(int x,int y,int z) 25 { 26 b[x]+=z; 27 b[y+1]-=z; 28 c_update(x,z); 29 c_update(y+1,-z); 30 } 31 int sum(int x) 32 { 33 int ans=0; 34 while(x>0) 35 { 36 ans+=c[x]; 37 x-=lowbit(x); 38 } 39 return ans; 40 } 41 int main() 42 { 43 cin>>n; 44 for(int i=1;i<=n;i++) 45 cin>>a[i]; 46 b[1]=a[1]; 47 c_update(1,b[1]); 48 for(int i=2;i<=n;i++) 49 { 50 b[i]=a[i]-a[i-1]; 51 c_update(i,b[i]); 52 } 53 cin>>q; 54 while(q--) 55 { 56 int x; 57 cin>>x; 58 if(x==1) 59 { 60 int y,z,w; 61 cin>>y>>z>>w; 62 update(y,z,w); 63 } 64 if(x==2) 65 { 66 int y; 67 cin>>y; 68 cout<<sum(y)<<endl; 69 } 70 } 71 return 0; 72 }
接着要介紹的就是利用樹狀數組來實現區間修改和區間查詢,這樣就可以規避很多必須要寫線段樹的情況
寫完之后發現,帥呆了。時間空間代碼量全部吊打線段樹
寫這種BIT的時候我們需要維護三個數組
long long delta[maxn]; //差分數組 long long deltai[maxn]; //delta*i long long sum[maxn];//原始前綴和
初始化的時候,直接計算前綴和就可以了,兩個樹狀數組都是用來存delta的
然后是修改區間操作,4次Update即可:
int y,z,w; cin>>y>>z>>w; update(delta,y,w); update(delta,z+1,-w); update(deltai,y,w*y); update(deltai,z+1,-w*(z+1));
查詢區間和的操作,直接在前綴和基礎上加減delta就可以了
long long suml=sum[y-1]+y*query(delta,y-1)-query(deltai,y-1); long long sumr=sum[z]+(z+1)*query(delta,z)-query(deltai,z); cout<<sumr-suml<<endl;
下面給出完整的代碼:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=200005,maxq=200005; 6 int n,q; 7 int lowbit(int x) 8 { 9 return x&(-x); 10 } 11 long long delta[maxn]; //差分數組 12 long long deltai[maxn]; //delta*i 13 long long sum[maxn];//原始前綴和 14 void update(long long *c,int x,int y) 15 { 16 while(x<=n) 17 { 18 c[x]+=y; 19 x+=lowbit(x); 20 } 21 } 22 long long query(long long *c,int x) 23 { 24 long long ans=0; 25 while(x>0) 26 { 27 ans+=c[x]; 28 x-=lowbit(x); 29 } 30 return ans; 31 } 32 int main() 33 { 34 cin>>n; 35 for(int i=1;i<=n;i++) 36 { 37 int x; 38 cin>>x; 39 sum[i]=sum[i-1]+x; 40 } 41 cin>>q; 42 while(q--) 43 { 44 int x; 45 cin>>x; 46 if(x==1) 47 { 48 int y,z,w; 49 cin>>y>>z>>w; 50 update(delta,y,w); 51 update(delta,z+1,-w); 52 update(deltai,y,w*y); 53 update(deltai,z+1,-w*(z+1)); 54 } 55 if(x==2) 56 { 57 int y,z; 58 cin>>y>>z; 59 long long suml=sum[y-1]+y*query(delta,y-1)-query(deltai,y-1); 60 long long sumr=sum[z]+(z+1)*query(delta,z)-query(deltai,z); 61 cout<<sumr-suml<<endl; 62 } 63 } 64 return 0; 65 }
其實介紹到這里已經差不多了,但是精彩的還在后面,二維樹狀數組!