二维树状数组

引入

树状数组在之前已经提到过了,但是在一些情况中,需要动态维护矩阵范围内的某些特点,这时一维树状数组就无法实现了。

原理

和一维树状数组类似,一维树状数组是把从 1 到 x 的区间拆成几个 lowbit 的序列来维护,二维树状数组是把所求的(0,0)到(x,y)的区间拆成几个 lowbit 的区间(见代码可理解)。

实现

与一维树状数组不同的是,添加或查找需要用两层循环枚举,见代码。

添加:

int add(int x,int y,int k)
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		for(int j=y;j<=m;j+=lowbit(j))
		{
			tree[i][j]+=k;
		}
	}
}

查找:

int ser(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+=tree[i][j];
		}
	}
	return ret;
}

例题

再给出一道模板题,可以尝试一下。

题意:

一个n*m的方格,初始时每个格子有一个整数权值。接下来每次有2种操作:
1.改变一个格子的权值;
2.求一个子矩阵中某种特定权值出现的个数。

输入:

第一行有两个数n,m。
接下来n行,每行m个数,第i+1行第j个数表示格子(i,j)的初始权值。
接下来输入一个整数q。
接下来q行,每行描述一个操作。
操作1:“1 x y c”(不含双引号)。表示将格子(x,y)的权值改成c(1<=x<=n,1<=y<=m,1<=c<=100)。
操作2:“2 x1 x2 y1 y2 c”(不含双引号,x1<=x2,y1<=y2)。表示询问所有满足格子权值为c,且x1<=x<=x2,y1<=y<=y2的格子(x,y)的个数。

输出:

对于每个操作2,按照在输入中出现的顺序,依次输出一行一个整数表示所求得的个数。

样例:

输入:

3 3
1 2 3
3 2 1
2 1 3
3
2 1 2 1 2 1
1 2 3 2
2 2 3 2 3 2

输出:

1
2

代码如下:

#include<bits/stdc++.h>
using namespace std;

int n,m,q,a[305][305],tree[305][305][105];
inline int lowbit(int x)
{
	return x&(-x);
}
inline int add(int x,int y,int k)
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		for(int j=y;j<=m;j+=lowbit(j))
		{
			tree[i][j][k]++;
		}
	}
}
inline int del(int x,int y,int k)
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		for(int j=y;j<=m;j+=lowbit(j))
		{
			tree[i][j][k]--;
		}
	}
}
inline int ser(int x,int y,int k)
{
	int ret=0;
	for(int i=x;i>=1;i-=lowbit(i))
	{
		for(int j=y;j>=1;j-=lowbit(j))
		{
			ret+=tree[i][j][k];
		}
	}
	return ret;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
            scanf("%d",&a[i][j]);
			add(i,j,a[i][j]);
		}
	}
    scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		int ch;
        scanf("%d",&ch);
		if(ch==1)
		{
			int x,y,c;
            scanf("%d%d%d",&x,&y,&c);
			del(x,y,a[x][y]);
			a[x][y]=c;
			add(x,y,c);
		}
		else
		{
			int x1,x2,y1,y2,c;
            scanf("%d%d%d%d%d",&x1,&x2,&y1,&y2,&c);
            printf("%d\n",ser(x2,y2,c)-ser(x1-1,y2,c)-ser(x2,y1-1,c)+ser(x1-1,y1-1,c));
		}
	}
}

如有错误求大佬指出,蒟蒻感激不尽qaq。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值