二维树状数组及(不会用到的)三维树状数组

二维树状数组及(不会用到的)三维树状数组

前置芝士

一维树状数组(lowbit)

二维树状数组

二维树状数组涉及到两种基本操作,修改矩阵中的一个点,查询子矩阵的和

首先是修改点的操作:

void update(int x,int y,int z){		//坐标为(x,y)的点增加z
	for(int i=x;i<=n;i+=lowbit(i))
    for(int j=y;j<=n;j+=lowbit(j))
        c[i][j]+=z;
}

然后是查询子矩阵的和,这里查询的是从左上角到目标点所形成的矩阵的元素和

int sum(int x,int y){
    int ret=0;
    for(int i=x;i>=1;i-=lowbit(i))
    for(int j=y;j>=1;j-=lowbit(j))
        ret+=c[i][j];
    return ret;
}

那么如果我要查具体的一个子矩阵,就需要给出左上角的点和右下角的点的坐标,然后:

int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
cout<<sum(x2,y2)-sum(x1-1,y2)-sum(x2,y1-1)+sum(x1-1,y1-1)<<endl;

就可以了

下面附上完整的二维树状数组的代码:

#include<iostream>
using namespace std;
const int maxn=1005;
const int maxm=1005;
int n,m;
int q;
int a[maxn][maxm];
int c[maxn][maxm];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int y,int z)
{
    for(int i=x;i<=n;i+=lowbit(i))
    for(int j=y;j<=m;j+=lowbit(j))
        c[i][j]+=z;
}

int sum(int x,int y)
{
    int ret=0;
    for(int i=x;i>=1;i-=lowbit(i))
    for(int j=y;j>=1;j-=lowbit(j))
        ret+=c[i][j];
    return ret;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        cin>>a[i][j];
        update(i,j,a[i][j]);
    }
    cin>>q;
    while(q--)
    {
        int x;
        cin>>x;
        if(x==1)
        {
            int y,z,w;
            cin>>y>>z>>w;
            update(y,z,w);
        }
        if(x==2)
        {
            int x1,y1,x2,y2;
            cin>>x1>>y1>>x2>>y2;
            cout<<sum(x2,y2)-sum(x1-1,y2)-sum(x2,y1-1)+sum(x1-1,y1-1)<<endl;
        }
    }
    return 0;
}

接下来我们对二维树状数组进行简单的拓展,将其拓展为修改矩形区间,查询单点的二维树状数组

其实就是把二维差分的思想引入进去,当然,如果不用树状数组直接用二维差分数组也是完全可以的,这个时候修改区间变成了O(1),查询点就变成了O(n),还是需要自己去权衡

二维树状数组的修改和查询的函数还是完全不用去变的

修改区间就要这么修改了:

void add(int x1,int y1,int x2,int y2,int w)
{
    update(x1,y1,w);            
	update(x2+1,y2+1,w);
	update(x2+1,y1,-w);
	update(x1,y2+1,-w);
}

这个东西虽然是类比一维情况得来的,但是你不要去想,去在纸上画一画,主对角线端点为正,负对角线端点为负,然后就很显然了

查询单点的话直接sum(x,y)即可

这里给出完整的代码:

#include<iostream>
using namespace std;
const int maxn=1005;
const int maxm=1005;
int n,m;
int q;
int a[maxn][maxm];
int d[maxn][maxm];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int y,int z)
{
    for(int i=x;i<=n;i+=lowbit(i))
    for(int j=y;j<=m;j+=lowbit(j))
        d[i][j]+=z;
}
void add(int x1,int y1,int x2,int y2,int w)
{
    update(x1,y1,w);
    update(x2+1,y2+1,w);
    update(x2+1,y1,-w);
    update(x1,y2+1,-w);
}
int sum(int x,int y)
{
    int ret=0;
    for(int i=x;i>=1;i-=lowbit(i))
    for(int j=y;j>=1;j-=lowbit(j))
        ret+=d[i][j];
    return ret;
}
int main()
{
    cin>>n>>m;
    cin>>q;
    while(q--)
    {
        int x;
        cin>>x;
        if(x==1)
        {
            int x1,y1,x2,y2,w;
            cin>>x1>>y1>>x2>>y2>>w;
            add(x1,y1,x2,y2,w);
        }
        if(x==2)
        {
            int x,y;
            cin>>x>>y;
            cout<<sum(x,y)<<endl;
        }
    }
    return 0;
}

最后思考如何区间修改+区间查询

类比之前一维数组的区间修改区间查询(这个博客没有),下面这个式子表示的是点(x, y)的二维前缀和:
∑ i = 1 x ∑ j = 1 y ∑ h = 1 i ∑ k = 1 j d [ h ] [ k ] \sum\limits_{i=1}^x\sum\limits_{j=1}^y\sum\limits_{h=1}^i\sum\limits_{k=1}^jd[h][k] i=1xj=1yh=1ik=1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值