「二维树状数组」题解 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);
	  }
	}
}
posted @ 2019-09-03 22:58  Luckyblock  阅读(205)  评论(0编辑  收藏  举报