传送门
Problem Description
题意大致如下
小腾有一块 n * m的土地,但最近小腾发现自己的庄稼经常被一群人偷,于是他决定安装一些监视器,找到所有的人,然后和他们谈判。
不过,小滕买的显示器不好,每个显示器只能监视一个长方形内的庄稼。Xiaoteng安装了p个监视器,每个监视器监视的矩形是已知的。 输入
有多个测试用例。 每种情况从一条包含两个整数n,m(1≤n,1≤m,n×m≤107)的线开始,表示土地面积。
第二行包含一个整数p(1≤p≤106),表示Xiaoteng安装的监视器的数量。后面是p行,每行表示一个矩形。每行包含四个整数x1、y1、x2和y2(1≤x1≤x2≤n,1≤y1≤y2≤m),表示矩形的左下角和右上角。
下一行包含一个整数q(1≤q≤106),它表示盗贼盗取物品的次数庄稼。这个后面是描述一个矩形的q行。每行包含四个整数x1、y1、x2和y2(1≤x1≤x2≤n,1≤y1≤y2≤m),表示矩形的左下角和右上角。
输出 对于每种情况,你应该打印q行。 每行包含是或否的意思都是小偷是否都可以看到。 样本输入 6 6 3 2 2 4 4 3 3 5 6 5
1 6 2 2 3 2 5 4 1 5 6 5 样本输出 YES NO提示
图中,红色实心矩形表示安装的显示器Xiaoteng,蓝色虚线矩形表示该区域将被盗。
1.题目分析
0.多组输入
1.首先题目给n * m 是 10 ^7 。
2.他要安装p个监控,每次要标记一个矩阵(左下角坐标(x1, y1) - > 右上角坐标(x2, y2)。
3.会有q(1≤q≤106),它表示盗贼盗取物品的次数庄稼。这个后面是描述一个矩形的q行。每行包含四个整数x1、y1、x2和y2,表示矩形的左下角和右上角。
4.每次查找是否能完全确保能看到小偷,即小偷的偷盗范围全在监控范围内。 二 . 解题思路 先想暴力做法,再想办法优化暴力
1.怎么存图? 首先二维地图是开不了,那么就只能开一维存图。在n * m 的地图中, 点(x, y)在地图中表示的是在第x行第y列,在此地图中 一行有m个, 用一维表示就是 x * m + y 。个人感觉原理解释的不太清楚
差不多就是离散化一下,用b[get(x, y)] 表示二维坐标(x,y)
int get(int x, int y)//把(x, y)坐标化为一维
{
return x * m + y;
}
2.安装p个监控,暴力的做法是从(x1, y1)遍历到(x2, y2),标记这些地方能被监控到,时间复杂度是(n ^ 2), 但安装操作次数过多,会TLE,优化的话就要用到二维差分(黑科技)来进行区域更新操作,每次操作会更新4个点。单次时间复杂度0(4),能过了。
操作代码如下:
a[get(x1, y1)] += 1;
a[get(x1, y2 + 1)] -= 1;
a[get(x2 + 1, y1)] -= 1;
a[get(x2 + 1, y2 + 1)] += 1;
p次操作后,在求出被监控覆盖的矩阵b。 坑点:某个点可能被标记了多次,但实际上只能被标记一次,所以,求被标记矩阵b的同时,要特殊处理一下
跑样例未处理前,出现了2
处理后
3.抓p次小偷,每次给出一个小偷活动的矩阵,可以求出小偷活动的区域面积an,再次区域内监控能覆盖的面积bn,如果an == bn,则小偷能被抓到,输出YES,反之亦然。 朴素的想法是,双重for循环求出小偷活动矩阵内监控能覆盖的面积an,
用公式求出小偷面积bn,在进行判断两面积是否相等。 但次数是真的多,该想法要优化,这是就可用二维前缀和来进行区间求和操作。
s[get(x2, y2)] - s[get(x1 - 1, y2)] - s[get(x2, y1 - 1)] + s[get(x1 - 1, y1 -1)];
2.题意总结
1.用差分来进行监控安装。
2.安装完后,根据差分数组a[ ] 求一遍监控的标记数组 b[ ](b中元素<= 1),在求一遍b 的前缀和数组。
3.根据前缀和数组求出每次小偷活动范围内能监控到的面积与小偷活动面积进行比较,若二者相等,则小偷能被抓到,输出YES,反之亦然。
3.代码实现
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e7 + 8;// n * m的最大范围
typedef long long ll;
int n, m, p, q,x1,y1,x2, y2;
// 差分 原矩阵 前缀和
int a[N], b[N], s[N], t;
int get(int x, int y)
{
return x * m + y;
}
int main()
{
while (cin >> n >> m) {//多组输入
scanf("%d", &p);
memset(a, 0, sizeof a);//初始化所有用过的数组
memset(b, 0, sizeof b);
memset(s, 0, sizeof s);
while(p--) {//装监控
//差分处理
scanf("%d%d%d%d", &x1,&y1,&x2,&y2);
a[get(x1, y1)] += 1;
a[get(x1, y2 + 1)] -= 1;
a[get(x2 + 1, y1)] -= 1;
a[get(x2 + 1, y2 + 1)] += 1;
}
//监控安装完后
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
//根据差分数组求原数组
b[get(i, j)] = a[get(i, j)] + b[get(i - 1, j)] + b[get(i, j - 1)] - b[get(i - 1, j - 1)];
//特殊处理一下
t= min(b[get(i, j)], 1);
//cout << t << " ";
//前缀和数组
s[get(i, j)] = t + s[get(i - 1, j)] + s[get(i, j - 1)] - s[get(i - 1, j - 1)];
}
//cout<< endl;
}
scanf("%d", &q);
//处理小偷
while (q--) {
scanf("%d%d%d%d", &x1,&y1,&x2,&y2);
int an = (x2 - x1 + 1) * (y2 - y1 + 1);//小偷活动面积
//用前缀和来求(小偷活动面积内)监控能覆盖的面积
int bn = s[get(x2, y2)] - s[get(x1 - 1, y2)] - s[get(x2, y1 - 1)] + s[get(x1 - 1, y1 -1)];
//cout << an << ' ' << bn << endl;
if (an == bn) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
很可惜,比赛时因为没注意多组输入,忘了要初始化,而没做出来。
比赛时,也因没注意多组输入而wa了很多发。