「二维树状数组」题解 P4054 【[JSOI2009]计数问题】
先无良宣传一下博客 \(wwwwww\)
文章列表 - 地灵殿 - 洛谷博客
知识点 : 多维树状数组 .
二维树状数组模板题 .
可以发现 , 矩阵中值的值域很小
\(w\le 100\)
考虑暴力思路,
构建 \(100\) 个二维树状数组,
分别存储各数值 ,
在矩阵中出现的次数.
-
对于单点修改操作:
将点 \((x,y)\) ,由值 \(a\) 修改为 \(b\).先对值 \(a\) 的树状数组 进行修改,
对应位置的出现次数 \(-1\) .再对值 \(b\) 的树状数组 进行修改,
对应位置的出现次数 \(+1\) . -
对于区间查询操作:
直接查询 对应子矩阵中
出现次数即可
另外 , \(bzoj\) 上的此题空间限制只有 \(64mb\)
要注意不能把所有变量,
都设置为 \(long\ long\) .
附 代码:
#include<cstdio>
#include<ctype.h>
#define ll long long
#define lowbit(x) -x&x
//=============================================================
int N,M,Q;
int map[301][301];
int tree[101][301][301];
//=============================================================
inline int read()
{
int fl=1,w=0;char ch=getchar();
while(!isdigit(ch) && ch!='-') ch=getchar();
if(ch=='-') fl=-1;
while(isdigit(ch)){w=w*10+ch-'0',ch=getchar();}
return fl*w;
}
inline void add(int type,int x,int y,int z)//修改操作,将此点权值设为type,并使其次数+=z
{
map[x][y]=type;//更新
for(int i=x;i<=N;i+=lowbit(i))//进行拆分
for(int j=y;j<=M;j+=lowbit(j))
tree[type][i][j]+=z;
}
inline ll sum(int type,int x,int y)//查询矩阵(1,1)--(x,y)中值type的出现次数
{
int ret=0;
for(int i=x;i;i-=lowbit(i))//进行拆分
for(int j=y;j;j-=lowbit(j))
ret+=tree[type][i][j];//更新
return ret;
}
//=============================================================
signed main()
{
N=read(),M=read();
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)//构造初始树状数组
{
int w=read();
add(w,i,j,1);
}
Q=read();
for(int i=0;i<Q;i++)
{
int opt=read();
if(opt==1)//修改操作
{
int x=read(),y=read(),type=read();
add(map[x][y],x,y,-1);//原值出现次数-1
add(type,x,y,1);//新值出现次数+1
}
else
{
int x1=read(),x2=read(),y1=read(),y2=read(),type=read();
ll ans1=sum(type,x2,y2),ans2=sum(type,x1-1,y1-1);//计算子矩阵的值
printf("%lld\n",ans1-sum(type,x1-1,y2)-sum(type,x2,y1-1)+ans2);
}
}
}
作者@Luckyblock,转载请声明出处。