引入
树状数组在之前已经提到过了,但是在一些情况中,需要动态维护矩阵范围内的某些特点,这时一维树状数组就无法实现了。
原理
和一维树状数组类似,一维树状数组是把从 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。