[The 17th Zhejiang Provincial Collegiate Programming Contest]-H. Huge Clouds (2020年浙江省賽,幾何,思維)
題面:
題意:
有\(\mathit n\)個光源和\(\mathit m\)個擋板,在X軸上的點到某個光源的線段中如果不經過任何擋板,則表明該點可以被那個光源的找到。如果某個點不會被任何一個光源找到,那麼該點是黑暗的。現在問你X軸上黑暗部分的長度。
思路:
我們枚舉每一個光源和每一個線段,通過基礎的幾何算法可以求出無法被該點光源照亮的區間。
那麼共有\(N*M\)個區間,我們需要快速的求出每一個光源對應的區間的交集。
我們不妨將區間拆爲兩個事件,每一個事件保存3個信息,分別爲:是該區間的左or右端點,x軸上的位置,光源編號,我們即可對所有事件\(\{pos,tpye,i\}\)按照位置升序排序後如下處理:
(1) 當 \(\mathit t\) 從 \(l − ϵ\) 增大至 \(\mathit l\) 時,接下來的位置裏,點光源\(i\)被遮擋次數增加 1。
(2) 當 t 從 \(\mathit r\) 增大至 \(r + ϵ\) 時,接下來的位置裏,點光源 \(\mathit i\) 被遮擋次數減少 1。
用桶數組維護每個點光源被遮擋的次數以及被至少遮擋一次的點光源數量\(num\)。
若 \(num = n\),則該事件到上一個事件中間的部分未被照亮。
時間複雜度 \(O(n*m*log(nm))\)。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const db eps = 1e-6;
int sgn(db x)
{
if (fabs(x) < eps) {
return 0;
}
return x > 0 ? 1 : -1;
}
struct Point {
db x, y;
Point() {}
Point(db _x, db _y)
{
x = _x; y = _y;
}
Point operator - (const Point &b) const
{
return Point(x - b.x, y - b.y);
}
db operator * (const Point &b) const
{
return x * b.x + y * b.y;
}
db operator ^ (const Point &b) const
{
return x * b.y - y * b.x;
}
bool operator < (const Point &b) const
{
return (sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x);
}
};
Point getpoint(const Point &a, const Point &b, const Point &c, const Point &d)
{
Point res;
db a1, b1, c1, a2, b2, c2;
a1 = a.y - b.y, b1 = b.x - a.x, c1 = a.x * b.y - b.x * a.y;
a2 = c.y - d.y, b2 = d.x - c.x, c2 = c.x * d.y - d.x * c.y;
res.x = (b1 * c2 - b2 * c1) / (a1 * b2 - a2 * b1);
res.y = -(a1 * c2 - a2 * c1) / (a1 * b2 - a2 * b1);
return res;
}
struct Line {
Point s, e;
Line() {}
Line(Point _s, Point _e)
{
s = _s; e = _e;
}
pair<int, Point> operator &(const Line &b)const
{
Point res = s;
if (sgn((s - e) ^ (b.s - b.e)) == 0) {
if (sgn((s - b.e) ^ (b.s - b.e)) == 0) {
return make_pair(0, res); //重合
} else { return make_pair(1, res); } //平行
}
res = getpoint(s, e, b.s, b.e);
return make_pair(2, res);
}
bool point_on_line(Point p)
{
return sgn((p - s) ^ (e - s)) == 0;
}
bool point_on_seg(Point p)
{
return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (p - e)) <= 0;
}
void adjust()
{
if (sgn(s.y - e.y) > 0) {
swap(s, e);
}
}
};
int n, m;
Point a[505];
Line b[505];
int cnt[505];
vector<pair<double, double> > c[505];
#define mp make_pair
struct node {
double pos;
int tpye;
int id;
node() {}
node(double _pos, int _type, int _id)
{
pos = _pos;
tpye = _type;
id = _id;
}
bool operator < (const node &b) const
{
return sgn(pos - b.pos) < 0;
}
};
std::vector<node> v;
int main()
{
int t;
scanf("%d", &t);
pair<int, Point> res;
Line xz = Line(Point(-1, 0), Point(1, 0));
while (t--) {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%lf %lf", &a[i].x, &a[i].y);
}
for (int i = 1; i <= m; ++i) {
scanf("%lf %lf %lf %lf", &b[i].s.x, &b[i].s.y, &b[i].e.x, &b[i].e.y);
b[i].adjust();
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (b[j].point_on_line(a[i])) {
if (b[j].point_on_seg(a[i])) {
c[i].push_back(mp(-1e18, 1e18));
}
continue;
}
if (sgn(b[j].s.y - a[i].y) >= 0) {
continue;
}
if (sgn(a[i].y - b[j].e.y) > 0) {
double l, r;
res = Line(a[i], b[j].s) & xz;
l = res.second.x;
res = Line(a[i], b[j].e) & xz;
r = res.second.x;
if (sgn(l - r) > 0) {
swap(l, r);
}
c[i].push_back(mp(l, r));
} else {
double cp;
res = Line(a[i], b[j].s) & xz;
cp = res.second.x;
if (sgn((a[i] - b[j].s) ^ (b[j].e-b[j].s)) >= 0) {
c[i].push_back(mp(-1e18, cp));
} else {
c[i].push_back(mp(cp, 1e18));
}
}
}
}
for (int i = 1; i <= n; ++i) {
for (auto &x : c[i]) {
v.push_back(node(x.first, 0, i));
v.push_back(node(x.second, 1, i));
}
}
sort(v.begin(), v.end());
int num = 0;
double ans = 0;
double pre;
for (auto &now : v) {
if (num == n) {
ans += now.pos - pre;
}
if (now.tpye == 0) {
if (cnt[now.id]++ == 0) {
num++;
}
} else {
if (--cnt[now.id] == 0) {
num--;
}
}
pre = now.pos;
}
if (sgn(ans - 1e9) > 0) {
printf("-1\n");
} else {
printf("%.6f\n", ans);
}
for (int i = 1; i <= n; ++i) {
c[i].clear();
cnt[i] = 0;
}
v.clear();
}
return 0;
}