E-海啸-牛客竞赛

 

E - 海啸 - 牛客竞赛



时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
 

题目描述

有一个沿海地区,可以看作有n行m列的城市,第i行第j列的城市海拔为h[i][j]。
由于沿海,所以这个地区经常会发生海啸。
海啸发生时,部分城市会被淹没,具体来说,海水高度会达到d,因此海拔低于d的城市都会被淹没。
现在有q次询问,每次问你一个矩形区域中,有多少城市不会被淹没。

输入描述:

第一行三个整数n,m,d,具体含义见题目描述。
接下来n行,每行m个整数,其中第i行第j列的整数为h[i][j],具体含义见题目描述。
第n+2行一个整数q,表示询问数。
接下来q行,每行四个整数a,b,x,y,
表示询问从第a行第b列到第x行第y列的矩形地区中,有多少地区不会被淹没。
即有多少个i,j,满足 a≤i≤x,b≤j≤ya≤i≤x,b≤j≤y ,且 h[i][j]≥dh[i][j]≥d 。

输出描述:

共q行,第i行一个整数,表示第i个询问的答案。

示例1

输入

3 3 3
1 2 3
2 1 5
4 3 2
2
1 2 2 3
2 1 3 3

输出

2
3

备注:

1≤n×m≤10^6
1≤q≤10^5
0≤d,h[i][j]≤10^9
1≤a≤x≤n,1≤b≤y≤m

 

分析:

笔者刚看到题,海平面,城市,淹没等等字眼第一反应搜索,读完题面,发现不需要搜索因为题目中城市(点集)之间没有连通关系,城市之间是不相关的。

接下来下意识的觉得,哇,连搜索都不用,那不是好简单,看来这题是模拟题意。

顺着思路于是开始着手写代码,这个时候问题出现了行列范围是1≤n×m≤10^6,这个数组怎么开?如果直接开二维数组h[1000001][1000001]是不合理的,这个数组一共有10^6*10^6=10^12数量级的元素,空间爆掉了。那么这里说三种方法

1、官方给出的方法是定义一个**h二维指针数组,读取m,n后new重定义数组大小。

2、使用C++STL库封装的vector动态数组。

3、因为行*列<=10^6,可以用一个一维数组h[1000001],通过下标对n列的整除性实现降维。即每个h[i]相当于二维数组的s[i/n][i%n]。

数组考虑完了,下面开始模拟题意。

这时又有新的问题了,直接遍历是不可行的,如果每次询问都进行遍历那么操作次数是q*(x-a)*(y-b)在本题的数据规模下,时间复杂度太高了,会超出时间限制。那么需要进行预处理,使用前缀和的思想。用数组pre[x][y]的值表示,存放从1,1到x,y这个矩形内的不会被淹没的城市数,则矩形x1y1x2y2内的不会被淹没的城市数量可表示为 pre[x2][y2] - pre[x2][y1-1] - pre[x1-1][y2] + pre[x1-1][y1-1]。

(求pre[x][y]的值if(x >= d)pre[i][j] = 1 + pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1];
       else pre[i][j] = pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1];

就是反向使用上面的前缀和思路,即累加的思想

下面几种代码供大家参考

数组降维

//数组降维
#include<bits/stdc++.h>
using namespace std; 
int h[1000005],s[1000005];
int main(){
	int m,n,d,q,a,b,x,y,t,t1,t2,t3;
	scanf("%d%d%d",&m,&n,&d);
	for(int i=0;i<m*n;i++){
		scanf("%d",&h[i]);
	}
	t=0;
	for(int i=0;i<n;i++){
		if(h[i]>=d)t++;
		s[i]=t;
	}
	t=0;
	for(int i=0;i<m;i++){
		if(h[i*n]>=d)t++;
		s[i*n]=t;
	}	
	for(int i=1;i<m;i++){
		for(int j=1;j<n;j++){
			t=0;
			if(h[i*n+j]>=d)t=1;
			s[i*n+j]=t+s[(i-1)*n+j]+s[i*n+j-1]-s[(i-1)*n+j-1];
		}
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		scanf("%d%d%d%d",&a,&b,&x,&y);
		t=0;
		t=s[(x-1)*n+(y-1)];
		t3=s[(a-2)*n+(b-2)];
		if(a<2)t1=t3=0;else t1=s[(a-2)*n+(y-1)];
		if(b<2)t2=t3=0;else t2=s[(x-1)*n+(b-2)];		
		t=t-t2-t1+t3;	
		printf("%d\n",t);
	}
	return 0;
}

 

动态数组 

//动态数组
#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
vector<vector<int> > e;
int n,m;
int main()
{
    int d;
    scanf("%d%d%d",&n,&m,&d);
    e=vector<vector<int> >(n+1,vector<int>(m+1));
    for(int i=1,recv;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            scanf("%d",&recv);
            e[i][j]=(recv>=d?1:0)+e[i-1][j]+e[i][j-1]-e[i-1][j-1];
        }
    int q;
    scanf("%d",&q);
    for(int a,b,c,d;q--;)
    {
        scanf("%d%d%d%d",&a,&b,&c,&d);
        printf("%d\n",e[c][d]-e[a-1][d]-e[c][b-1]+e[a-1][b-1]);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值