树状数组总结

本小妞迷上赌 提交于 2020-03-30 09:18:43

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);
            */

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!