情书密码 (树状数组)

题目

问题描述

有消息称:绝恋找到了自己的Miss Right,正准备自己的表白。绝恋已经写好了情书,但为了避免其它人截获,他对情书进行加密。
为了打探绝恋的私密,你冒着生命危险终于搞到了这封情书。原以为可以轻易将情书解密,结果竟然发现聪明的绝恋并没有直接写出加密用的密码,而是在那粉红色的信纸背面写着“T=你的幸运数字”。
就这么放弃了吗?不,作为一个高智商的OIer,你决不轻言放弃。你还搞到了绝恋做密码时的草稿,通过一定的分析,你发现草稿中隐藏了绝恋的密码,具体规则如下:
草稿纸上写着一个N*M 的矩阵,每个位置都有一个数字C,绝恋对该矩阵不断进行操作,有时会修改某一位置的值,还不时计算出一个矩形内特定数字的个数作为密码的一部分,绝恋十分聪明,他进行了许多次这样的操作,因此密码也异常复杂,但是你已经下定决心要算出密码了,所以你一定要算出来!!

输入格式:

第一行有两个数字N,M
接下来N 行,每行M 个数,第i+1 行第j 个数表示格子(i,j)的初始值。
接下来一个整数Q
接下来Q 行,每行描述一个操作
操作1:1 X Y C,表示将格子(X,Y)的权值改为C
操作2:2 X1 X2 Y1 Y2 C,表示询问矩形内有多少个位置的权值为C。X1 X2分别为矩形横坐标中的最小值和最大值,Y1 Y2为矩形纵坐标中的最小值和最大值。

输出格式:

对于每一个操作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

数据范围:

对于30%的数据:1<=N,M<=30,Q<=50000
对于100%的数据:1<=N,M<=300,Q<=200000,C<=100
对于所有操作1:1<=X<=N,1<=Y<=M
对于所有操作2:1<=X1<=X2<=N,1<=Y1<=Y2<=M

思路

直接暴力枚举显然是不可能得满分的,因此我们可以换个思路:树状数组(如果你连树状数组都不知道是什么,那么打暴力当然是最明智的选择~(≧▽≦))
以下是暴力代码(引用自某位OIer

代码君
#include<bits/stdc++.h>
using namespace std;
const int N=300+5;
int n,m,q,a[N][N];
int x,y,xx,yy,z,f;
int main(){
//    freopen("test.in","r",stdin);
//    freopen("test2.out","w",stdout);
    cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	cin>>q;
	while(q--){
		cin>>f;
		if(f==1){
			cin>>x>>y>>z;
			a[x][y]=z;
		}
		else{
			cin>>x>>xx>>y>>yy>>z;
			int ans=0;
			for(int i=x;i<=xx;i++){
				for(int j=y;j<=yy;j++){
					if(a[i][j]==z)ans++;
				}
			}
			cout<<ans<<endl;
		}
	}
	return 0;
}

由于是一个矩阵,所以不难想到是一个二维树状数组。但不同的是,树状数组求的是前缀和,而本题需要查找某个指定数的个数,我们伟大的Huge曾经说过:“想不出来,你可以再加一维”,因此我们可以令加一个维度用来记录某个数出现的次数。
代码有注释哦~(^_−)☆

Elaina's code

Elaina's code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x3f
#define INF 0x3f3f3f3f
#define mst(a,b) memset(a,b,sizeof(a))
#define Elaina 0
const int N=305;
int a[N][N],c[N][N][N],n,m;//a[][]代表矩阵,存储原数据,c[][][]代表树状数组
int lowbit(int x){
	return x&(-x);
} 
void add(int x,int y,int past,int now){
	for(int i=x;i<=N;i+=lowbit(i)){
		for(int j=y;j<=N;j+=lowbit(j)){
			c[i][j][past]--;//删去原数据
			c[i][j][now]++;//加上现更新的数据
		}
	}
}
int getsum(int x,int y,int z){//指定数的个数
	int sum=0;
	for(int i=x;i>0;i-=lowbit(i)){
		for(int j=y;j>0;j-=lowbit(j)){
			sum+=c[i][j][z];
		}
	}
	return sum;
}
signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		cin>>a[i][j];
			add(i,j,0,a[i][j]);//将0改为输入数据(初始化)
		}
	}
	int t=0;
	cin>>t;
	while(t--){
		int K;
		cin>>K;
		if(K==1){
			int x,y,z;
			cin>>x>>y>>z;
			add(x,y,a[x][y],z);//将原数据改为更新的数据
			a[x][y]=z;//原数组也要更新,因为不加会WA(划掉),因为如果要再更新a[x][y]会导致a[x][y]的past对应不上,不能删去
		}
		if(K==2){
			int x1,x2,y1,y2,z,sum=0;
			cin>>x1>>x2>>y1>>y2>>z;
			sum=getsum(x2,y2,z)-getsum(x1-1,y2,z)-getsum(x2,y1-1,z)+getsum(x1-1,y1-1,z);//二维前缀和计算公式
			cout<<sum<<"\n";
		}
	}
    return Elaina;
}

都看到这了,真的不点个赞吗(>ω<*)

posted @ 2024-02-18 20:08  Elaina_0  阅读(44)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end