这里丢一篇很好的blogs
https://www.cnblogs.com/hsd-/p/6139376.html
其实树状数组的是实现是根据二进制的
主要的函数有这三个
int lowbit(int x) { return x&(-x); }
这个是返回原数上去除末尾一个1的数
such as
t=6(0110)
-t=-6=(1001+1)=(1010)
t&(-t)=(0010)=2 得到这个有什么用呢,继续看
现在定义每一列的顶端结点C[]数组
如下图
C[i]代表 子树的叶子结点的权值之和// 这里以求和举例
如图可以知道
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];
将C[]数组的结点序号转化为二进制
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=8时,k=3;
举个例子 i=7;
sum[7]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7] ; //前i项和
C[4]=A[1]+A[2]+A[3]+A[4]; C[6]=A[5]+A[6]; C[7]=A[7];
可以推出: sum[7]=C[4]+C[6]+C[7];
序号写为二进制: sum[(111)]=C[(100)]+C[(110)]+C[(111)];
再举个例子 i=5
sum[7]=A[1]+A[2]+A[3]+A[4]+A[5] ; 前i项和
C[4]=A[1]+A[2]+A[3]+A[4]; C[5]=A[5];
可以推出: sum[5]=C[4]+C[5];
序号写为二进制: sum[(101)]=C[(100)]+C[(101)];
所以说我们可以通过对i加减lowbit(i),就能实现得到111 110 100 000这样的数
int add(int i,int x) { while (i<=n) { c[i]+=x; i+=lowbit(i); } }
这个呢是树状数组实现单点修改
1 int sum(int i) 2 { 3 int res=0; 4 while (i>=1) 5 { 6 res+=c[i]; 7 i-=lowbit(i); 8 } 9 return res; 10 }
这个是实现区间求和查询跟上面的是一样的意识
1.树状数组求逆序对
#include<iostream> #include<algorithm> using namespace std; struct sb { int z,num; }t[1001]; int c[1001],n; bool cmp(sb a,sb b) { return a.z<b.z?true:false; } int lowbit(int x) { return x&(-x); } int add(int i,int x) { while (i<=n) { c[i]+=x; i+=lowbit(i); } } int sum(int i) { int res=0; while (i>=1) { res+=c[i]; i-=lowbit(i); } return res; } int main () { cin>>n; for (int i=1;i<=n;i++) { cin>>t[i].z; t[i].num=i; } sort(t+1,t+1+n,cmp); int ans=0; for (int i=1;i<=n;i++) { add(t[i].num,1); ans+=i-sum(t[i].num); } cout<<ans; }
2.树状数组优化最长上升下降序列
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int a[100001]; 6 struct sb 7 { 8 int num,val; 9 }b[100001]; 10 int n,t[100001*4]; 11 bool cmp (sb a,sb b) { return a.val<b.val?true:false; } 12 int lowbit(int x) { return x&(-x); } 13 void add(int i,int k) 14 { 15 while (i<=n) 16 { 17 t[i]=max(t[i],k); 18 i+=lowbit(i); 19 } 20 } 21 int find(int i) 22 { 23 int ans=-1e9; 24 while (i) 25 { 26 ans=max(ans,t[i]); 27 i-=lowbit(i); 28 } 29 return ans; 30 } 31 int main () 32 { 33 int ans=0; 34 cin>>n; 35 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 36 for (int j=1;j<=n;j++) scanf("%d",&b[j].val),b[j].num=j; 37 sort(b+1,b+1+n,cmp); 38 for (int i=1;i<=n;i++) a[i]=b[a[i]].num; 39 for (int i=n;i>=1;i--) 40 { 41 int nt=find(a[i]); 42 add(a[i],++nt); 43 ans=max(nt,ans); 44 } 45 cout<<ans; 46 }
树状树,区间修改单点查询,差分数组
1 #include<iostream> 2 using namespace std; 3 int a[500001],b[500001],t[1000000]; 4 int n,m; 5 int lowbit(int x){return x&(-x);} 6 void add(int i,int k){for (i;i<=n;i+=lowbit(i)) t[i]+=k;} 7 int get(int i,int ans){for (i;i;i-=lowbit(i)) ans+=t[i]; return ans;} 8 int main () 9 { 10 cin>>n>>m; 11 for (int i=1;i<=n;i++) 12 cin>>a[i],b[i]=a[i]-a[i-1],add(i,b[i]); 13 for (int i=1,op,x,y,z;i<=m;i++) 14 { 15 cin>>op; 16 if (op==1) 17 { 18 cin>>x>>y>>z; 19 add(x,z); 20 add(y+1,-z); 21 } 22 else 23 { 24 cin>>x; 25 cout<<get(x,0)<<endl; 26 } 27 } 28 29 }