http://www.java3z.com/cwbwebhome/article/article1/1369.html推荐的博文
referrence:http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binaryIndexedTrees
对于正整数,我们定义lowbit(x)为x的二进制表达式中最右边1所对应的值。对于节点i,如果它是左子节点,那么父节点的编号就是i+lowbit(i),如果它是右子节点,那么父节点的编号是i-lowbit(i),构造一个辅助数组C,其中 Ci=A[i-lowbit(i)+1]+...+A[i]。
前缀和S[i]:顺着节点i往左走,边走边“往上爬”,把沿途经过的C[i]累加起来
修改一个A[i],从C[i]开始往右走,边走边“往上爬”,沿途修改所有节点对应的C[i]
注意:树状数组的下标永远是从1开始的
树状数组求逆序对的原理:
前面有i-1个数,把每次输入的数看作树状数组的下标,设置增加的变量为1,算其前缀和(有多少个1就有多少个顺序对),然后减去顺序对就是答案,方案有两种(本质是一样的):
1、
ans+=(i-1-sum(a));
add(a);
2、
add(a);
ans+=(i-sum(a));
模板:
#define MARK 50005 int c[MARK],n; int lowbit(int x) { return x&-x; } int sum(int x) //计算最大下标为s的前缀和 { int ret=0; while(x>0) { ret+=c[x];x-=lowbit(x); } return ret; } void add(int x,int d) //下标x的数增加d,对应的c数组也会有变化 { while(x<=n) { c[x]+=d;x+=lowbit(x); } }
如果我们要计算某个下标x的值,我们可以sum(x)-sum(x-1),时间为O(2*log(N)),但是我们有更好的算法:
/*时间复杂度为:(c*logN),c小于1*/ int readSingle(int x){ int ret = c[x]; //我们知道c[x]是左子树元素之和(包含x为下标的元素) if (x > 0){ int z = x - (x&-x); //我们知道c[x]=A[x-lowbit[x]+1]+...+A[i],z很明显是前一个元素 x--; while (x != z){ ret -= c[x]; x -= (x & -x); } } return ret; }
附加个论文图(假设计算下标为12的数):
假设我们要求某个区间内和等于val的下标(规定元素都是非负的),
看到了吗?越左的tree[i]越大。所以我们可以用二分查找来求:
// if in tree exists more than one index with a same // cumulative frequency, this procedure will return // some of them (we do not know which one) // bitMask - initialy, it is the greatest bit of MaxVal // bitMask store interval which should be searched int find(int cumFre){ int idx = 0; // this var is result of function while ((bitMask != 0) && (idx < MaxVal)){ // nobody likes overflow :) int tIdx = idx + bitMask; // we make midpoint of interval if (cumFre == tree[tIdx]) // if it is equal, we just return idx return tIdx; else if (cumFre > tree[tIdx]){ // if tree frequency "can fit" into cumFre, // then include it idx = tIdx; // update index cumFre -= tree[tIdx]; // set frequency for next loop } bitMask >>= 1; // half current interval } if (cumFre != 0) // maybe given cumulative frequency doesn't exist return -1; else return idx; } // if in tree exists more than one index with a same // cumulative frequency, this procedure will return // the greatest one int findG(int cumFre){ int idx = 0; while ((bitMask != 0) && (idx < MaxVal)){ int tIdx = idx + bitMask; if (cumFre >= tree[tIdx]){ // if current cumulative frequency is equal to cumFre, 考虑存在0元素 // we are still looking for higher index (if exists) idx = tIdx; cumFre -= tree[tIdx]; } bitMask >>= 1; } if (cumFre != 0) return -1; else return idx; }
二维:
假设每个格子代表一个数A[i][j],i是横坐标,j是纵坐标,左上角的坐标为(1,1)我们要求红色区域元素之和,设sum(i,j)表示以i,j坐标为右下角坐标,以0,0为左上角坐标矩形内的元素之和,
sum(c,d):绿色+黄色+红色+蓝色
sum(c,b-1):绿色+蓝色
sum(a-1,d):绿色+黄色
sum(a-1,b-1):绿色
则红色区域元素之和=sum(c,d)-sum(c,b-1)-sum(a-1,d)+sum(a-1,b-1)
#define N 1005 int C[N][N]; //使用C之前,先清0 int sum(int x,int y) //x是横坐标,y是纵坐标 { int ret=0,i,j; for(i=x;i>0;i-=(i&-i)) for(j=y;j>0;j-=(j&-j)) ret+=C[i][j]; return ret; } void add(int x,int y,int d) //d是需要增加的值 { int i,j; for(i=x;i<N;i+=(i&-i)) for(j=y;j<N;j+=(j&-j)) C[i][j]+=d; }
三维:
#define N 105 int c[N][N][N],n,m; int sum(int x,int y,int z) { int ret=0,i,j,k; for(i=x;i;i-=(i&-i)) for(j=y;j;j-=(j&-j)) for(k=z;k;k-=(k&-k)) ret+=c[i][j][k]; return ret; } void add(int x,int y,int z,int d) { int i,j,k; for(i=x;i<=n;i+=(i&-i)) for(j=y;j<=n;j+=(j&-j)) for(k=z;k<=n;k+=(k&-k)) c[i][j][k]+=d; } /* add(x2+1,y2+1,z2+1,1); add(x1,y2+1,z2+1,1); add(x2+1,y1,z2+1,1); add(x1,y1,z2+1,1); add(x1,y2+1,z1,1); add(x2+1,y1,z1,1); add(x2+1,y2+1,z1,1); add(x1,y1,z1,1); */
来源:https://www.cnblogs.com/A-way/archive/2013/05/20/3089637.html