题意
二维平面上,有n个已知矩形,我们现需选择一个点,以该点为中心,d(已知)为步长,上下左右扩展形成的点网中的任意一点不能落到任意矩形中。
解法分析
因为步长为d,只要从(0, 0)-(d-1, d-1)中找点。
以下长表示x坐标长度,宽表示y坐标长度
将矩形分为三种类型:
1.长宽均大于等于d,输出为no。
2.长或者宽只有一个小于d,则只对我们选点的y或者x坐标有一个限定。
3.长宽小于d,则在(0,0)-(d-1,d-1)对应的矩形我们是不能取的。
问题转化为了我有一个大矩形区域,并且有n个矩形覆盖,找出区域中哪个点没被覆盖?
矩形覆盖的并,用扫描线可以解决。
代码
#include <bits/stdc++.h>
using namespace std;
struct seg
{
int l, r, v;
};
vector<seg> q[100010];
struct segtree
{
int minn;
};
struct node
{
int minn, min_id;
int lazy;
} tree[400010];
void build(int x, int l, int r)
{
if (l == r)
{
tree[x] = {0, l, 0};
return;
}
int mid = (l + r) >> 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
tree[x] = {0, l, 0};
}
void push_up(int x)
{
if (tree[x << 1].minn < tree[x << 1 | 1].minn)
tree[x].minn = tree[x << 1].minn,
tree[x].min_id = tree[x << 1].min_id;
else
tree[x].minn = tree[x << 1 | 1].minn,
tree[x].min_id = tree[x << 1 | 1].min_id;
}
void push_down(int x)
{
if (!tree[x].lazy)
return;
tree[x << 1].minn += tree[x].lazy;
tree[x << 1].lazy += tree[x].lazy;
tree[x << 1 | 1].minn += tree[x].lazy;
tree[x << 1 | 1].lazy += tree[x].lazy;
tree[x].lazy = 0;
}
void change(int x, int l, int r, int ql, int qr, int v)
{
// cout << l << ' ' << r << ' ' << ql << ' ' << qr << endl;
if (ql <= l && r <= qr)
{
tree[x].lazy += v;
tree[x].minn += v;
return;
}
push_down(x);
int mid = (l + r) >> 1;
if (mid >= ql)
change(x << 1, l, mid, ql, qr, v);
if (mid < qr)
change(x << 1 | 1, mid + 1, r, ql, qr, v);
push_up(x);
}
int main()
{
int n, d;
cin >> n >> d;
bool flag = 0;
for (int i = 1; i <= n; ++i)
{
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
// cout << x1 <<' ' << y1 << ' ' << x2 << ' ' << y2<< endl;
if (x2 - x1 >= d && y2 - y1 >= d)
flag = 1;
else if (x2 - x1 >= d)
{
y1 = (y1 % d + d) % d;
y2 = ((y2 - 1) % d + d) % d;
if (y1 <= y2)
q[0].push_back({y1, y2, 1});
else
q[0].push_back({y1, d - 1, 1}),
q[0].push_back({0, y2, 1});
}
else if (y2 - y1 >= d)
{
x1 = (x1 % d + d) % d;
x2 = (x2 % d + d - 1) % d + 1;
if (x1 < x2)
q[x1].push_back({0, d - 1, 1}),
q[x2].push_back({0, d - 1, -1});
else
q[x1].push_back({0, d - 1, 1}),
q[0].push_back({0, d - 1, 1}),
q[x2].push_back({0, d - 1, -1});
}
else
{
x1 = (x1 % d + d) % d;
x2 = (x2 % d + d - 1) % d + 1;
y1 = (y1 % d + d) % d;
y2 = ((y2 - 1) % d + d) % d;
if (x1 < x2 && y1 <= y2)
{
q[x1].push_back({y1, y2, 1});
q[x2].push_back({y1, y2, -1});
}
else if (x1 < x2)
{
q[x1].push_back({y1, d - 1, 1});
q[x1].push_back({0, y2, 1});
q[x2].push_back({y1, d - 1, -1});
q[x2].push_back({0, y2, -1});
}
else if (y1 <= y2)
{
q[x1].push_back({y1, y2, 1});
q[0].push_back({y1, y2, 1});
q[x2].push_back({y1, y2, -1});
}
else
{
q[x1].push_back({y1, d - 1, 1});
q[x1].push_back({0, y2, 1});
q[0].push_back({y1, d - 1, 1});
q[0].push_back({0, y2, 1});
q[x2].push_back({y1, d - 1, -1});
q[x2].push_back({0, y2, -1});
}
}
// cout << x1 <<' ' << y1 << ' ' << x2 << ' ' << y2<< endl;
}
if (flag)
cout << "NO";
else
{
build(1, 0, d - 1);
// cout << "1";
for (int i = 0; i <= d - 1; ++i)
{
for (auto j : q[i])
change(1, 0, d - 1, j.l, j.r, j.v);
// cout << i << ' ' << j.l << ' ' << j.r << ' ' << j.v << endl;
// cout << "1";
if (!tree[1].minn)
{
cout << "YES" << endl
<< i << ' ' << tree[1].min_id;
flag = 1;
break;
}
}
if (!flag)
cout << "NO";
}
return 0;
}
扫描线算法
二维平面上求矩形面积并。
先考虑暴力的情况,对于一个矩形覆盖,直接x,y枚举覆盖。
稍微不暴力一点,对x枚举,每个x建立线段树,进行区间修改,然后统计。
每个x建立线段树果然还是有点凶残,能不能只建一个线段树然后统计呢?
当然可以。
考虑到之前我们是对x枚举修改y区间,但如果我们对x差分,只在矩形左边右边修改区间。然后对x从头到尾扫描,修改线段树,并统计答案,这就大大优化了时间和空间。
这就是扫描线算法。
小结
线段树不熟练,代码力不够,导致这题想出解法但比赛中没过。这题细节还是挺多的。先挖个坑,复习线段树。