模板总结——树状数组

一维树状数组

对于数组A[1…n],在O(logn)的时间内完成以下任务:
(1)给A[i]的值加上一个数
(2)求A[1]+A[2]+…+A[i]的和

说明

lowbit(i)表示i在二进制表示法中最后一个’1‘所在位置的值,如lowbit(12)=4,因为12=1100(2)。
lowbit(i)=i&(-1)。

Tree[i]表示A[i]的前lowbit(i)个数的和,包括A[i]。如Tree[12]=A[12]+A[11]+A[10]+A[9]。

i-=lowbit(i)等价于将i的二进制的最后一个’1‘减去。i的二进制中最多有logn个’1‘,所以时间复杂度是O(logn)的。
i+=lowbit(i)等价于将i的二进制的最后一个’1‘补为’0‘。

当i为0时,i+=lowbit(i)依旧会为0,即死循环。所以,插入的数值不能为0。

代码

void add(int i,int delta)
{
    while(i<maxn)
    {
        Tree[i]+=delta;
        i+=lowbit(i);
    }
}
int sum(int i)
{
    int ans=0;
    while(i)
    {
        ans+=Tree[i];
        i-=lowbit(i);
    }
}

树状数组实现区间更新、单点查询

任务:
(1)区间[a,b]的值都加x
(2)求区间具体某个位置的数值
树状数组的优势在于快速进行单点修改和求前缀和。而要实现区间更新和单点查询,也只能通过转化成单点修改和求前缀和实现。
解决:
用两次单点修改来实现区间修改:add(a,x)和add(b+1,-x).
用求前缀和来实现单点查询:sum(i).

二维树状数组

任务:
(1)对矩阵中的某个数加上一个整数
(2)查询某个子矩阵的和

Tree[i][j]表示由从第i行开始的前lowbit(i)行,从第j列开始的前lowbit(j)列组成的子矩阵的和。

求子矩阵第x1行到第x2行,第y1列到第y2列的和:
ans=sum(x2,y2)-sum(x2,y1-1)-(x1-1,y2)+sum(x1-1,y1-1)。

代码

void add(int i,int j,int delta)
{
    A[i][j]+=delta;
    for(int x=i;x<A.length;x+=lowbit(x))
        for(int y=j;y<A[i].length;y+=lowbit[y])
        Tree[x][y]+=delta;
}
int sum(int i,int j)
{
    int ans=0;
    for(int x=i;x;x-=lowbit(x))
        for(int y=j;y;y-=lowbit(y))
        ans+=Tree[x][y];
    return ans;
}

猜你喜欢

转载自blog.csdn.net/qq_37685156/article/details/80246622