我们假设一个数组A [n]的,当一个A[N] = 0的时候表示数字N没有在集合中出现过,当一个A[N] = 1时则出现过.A对应的树状数组为C [N]。
-
树状数组中添加函数:在求逆序数时,要添加数字时(A [1],1),即将该数加一,意思是将数字n加入到集合。
-
树状数组中总和函数:用于求集合中小于等于数字a的元素个数,所以最后要求比数字a大的元素可以用I-sum([I] -1)(I表示目前集合中的元素个数)
(被CSDN网站乱码气死
lowbit函数:二进制很强,今后一定要多观察二进制
I&( - I);
这个运算得到的值为K,K就是从我的末尾开始0的个数
k +我得到当前节点的父亲节点的位置
ki得到上一个父亲节点的位置
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 =(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 [1] = A [I-2 ^ K + 1] + A [I-2 ^ K + 2] + ...... A [1]; (K为我的二进制中从最低位到高位连续零的长度)例如I = 8时,K = 3;
区间查询
下面利用C [I]数组,求甲数组中前我项的和
举个例子i = 7;
sum [7] = A [1] + A [2] + A [3] + A [4] + A [5] + A [6] + A [7]; 前我项和
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 [5] = A [1] + A [2] + A [3] + A [4] + A [5]; 前我项和
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)];
细细观察二进制树状数组追其根本就是二进制的应用
结合代码
int getsum(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=C[i];
return ans;
}
对于i = 7进行演示
7(111) ans + = C [7]
lowbit(7)= 001 7-lowbit(7)= 6(110) ans + = C [6]
lowbit(6)= 010 6-lowbit(6)= 4(100) ans + = C [4]
lowbit(4)= 100 4-lowbit(4)= 0(000)
对于i = 5进行演示
5(101) ans + = C [5]
lowbit(5)= 001 5-lowbit(5)= 4(100) ans + = C [4]
lowbit(4)= 100 4-lowbit(4)= 0(000)
单点更新
当我们修改A []数组中的某一个值时应当如何更新C []数组呢?
回想一下区间查询的过程,再看一下上文中列出的图
结合代码分析
void add(int x,int y)
{
for(int i=x;i<=n;i+=lowbit(i))
tree[i]+=y;
}
//可以发现 更新过程是查询过程的逆过程
//由叶子结点向上更新C[]数组
当更新A [1]时需要向上更新C [1],C [2],C [4],C [8]
C [1],C [2],C [4],C [8]
写为二进制C [(001)],C [(010)],C [(100)],C [(1000)]
1(001) C [1] + = A [1]
lowbit(1)= 001 1 + lowbit(1)= 2(010) C [2] + = A [1]
lowbit(2)= 010 2 + lowbit(2)= 4(100) C [4] + = A [1]
lowbit(4)= 100 4 + lowbit(4)= 8(1000) C [8] + = A [1]
实现代码:
#include <iostream>
#include <string>
using namespace std;
#define N 1010
int c[N];
int n;
int lowbit(int i)
{
return i&(-i);
}
int insert(int i,int x)
{
while(i<=n){
c[i]+=x;
i+=lowbit(i);
}
return 0;
}
int getsum(int i)
{
int sum=0;
while(i>0){
sum+=c[i];
i-=lowbit(i);
}
return sum;
}
void output()
{
for(int i=1;i<=n;i++) cout<<c[i]<<" ";
cout<<endl;
}
int main()
{
while(cin>>n){
int ans=0;
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++){
int a;
cin>>a;
insert(a,1);
ans+=i-insert(a);//统计当前序列中大于a的元素的个数
}
cout<<ans<<endl;
}
return 0;
}
参考:
https://blog.csdn.net/Small_Orange_glory/article/details/81290634