2019中山大学程序设计竞赛 Monitor(HDU 6514 二维前缀和)

题目链接:

Monitor

 

题意:

有 n*m 的区域,现在有 p 个摄像头,每个摄像头可以监控左上角为(axi,ayi),右下角为(bxi,byi)的矩形区域。有 q 个询问,每次给定一个左上角为(ax,ay),右下角为(bx,by)的矩形区域,问这个区域是不是都在监控范围内。n*m <= 1e7 。

 

思路:

总思路为将监控覆盖的区域全标记为1,否则为0。查询时,若所查询矩形内1的个数恰好等于其格数就输出YES,否则输出NO。

现在的问题是如何标记,如何查询。

标记

(将左上角为(x1,y1),右下角为(x2,y2)的矩形区域标记为1):

将(x2,y2)和(x1-1,y1-1)置为1,(x1-1,y2)和(x2,y1-1)置为0。再先每行从右往左做前缀和,每列从下到上做前缀和。

例:将(2,3)到(4,5)更新:(最左上角为(1,1))

第一步:O(1)赋值

0100-10
000000
000000
0-10010
000000
000000

第二步:按行求前缀和(每行从右到左)

00-1-1-10
000000
000000
001110
000000
000000

第三步:按列求前缀和(每列从上到下)

000000
001110
001110
001110
000000
000000

可以看到通过这几部可以成功将一个矩形区域赋1。那么我们先按步骤一把输入的监控区域标记。再对全标记好的图按步骤二、三操作,即可将监控区域全部标记。(标记完后会有格子的值>1,因为可能被覆盖到多次,将其值变为1)

再对标记好的图做二维前缀和,使得(x,y)格子中的数代表(0,0)到(x,y)的矩形区域中1的个数。

按动态规划的思路,sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+mp[i][j]。mp[i][j]表示该点是0还是1。

 

查询

若矩形区域为(x1,y1)到(x2,y2),ans = sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]。

 

Tips:不能用二维vector存,就算每次清空内存都会MLE,只能用一维数组,根据坐标计算每点的标号即可。

 

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAX = 3e7 + 100;

int n, m;
int p, q;
int mp[MAX];

int main()
{
	int x1, y1, x2, y2;
	while (scanf("%d%d", &n, &m) != EOF)
	{
		scanf("%d", &p);
		for (int i = 0; i <= (n + 1)*(m + 1); i++) {
			mp[i] = 0;
		}
		//标记
		for (int i = 0; i < p; i++) {
			scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
			mp[x2*(m + 1) + y2]++;
			if (x1 - 1 >= 1)
				mp[(x1 - 1)*(m + 1) + y2]--;
			if (y1 - 1 >= 1)
				mp[x2*(m + 1) + y1 - 1]--;
			if ((x1 - 1 >= 1) && (y1 - 1 >= 1))
				mp[(x1 - 1)*(m + 1) + y1 - 1]++;
		}
		//每行从右往左做前缀和
		for (int i = 1; i <= n; i++) {
			for (int j = m - 1; j >= 1; j--) {
				mp[i*(m + 1) + j] += mp[i*(m + 1) + j + 1];
			}
		}
		//每列从下到上做前缀和
		for (int i = 1; i <= m; i++) {
			for (int j = n - 1; j >= 1; j--) {
				mp[j*(m + 1) + i] += mp[(j + 1)*(m + 1) + i];
			}
		}
		//计算二维前缀和
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				mp[i*(m + 1) + j] = mp[(i - 1)*(m + 1) + j] + mp[i*(m + 1) + j - 1] - mp[(i - 1)*(m + 1) + j - 1] + (mp[i*(m + 1) + j] > 0 ? 1 : 0);
			}
		}
		scanf("%d", &q);
		while (q--) {
			scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
			//查询
			int tot = mp[x2*(m + 1) + y2] - mp[(x1 - 1)*(m + 1) + y2] - mp[x2*(m + 1) + y1 - 1] + mp[(x1 - 1)*(m + 1) + y1 - 1];
			if (tot == (x2 - x1 + 1)*(y2 - y1 + 1)) {
				printf("YES\n");
			}
			else {
				printf("NO\n");
			}
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值