题目链接:
题意:
有 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)赋值
0 | 1 | 0 | 0 | -1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
0 | -1 | 0 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
第二步:按行求前缀和(每行从右到左)
0 | 0 | -1 | -1 | -1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
第三步:按列求前缀和(每列从上到下)
0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 1 | 0 |
0 | 0 | 1 | 1 | 1 | 0 |
0 | 0 | 1 | 1 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 |
可以看到通过这几部可以成功将一个矩形区域赋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;
}