bzoj 4445 小凸想跑步 - 半平面交

时间:2022-08-27 14:24:13

题目传送门

  vjudge的快速通道

  bzoj的快速通道

题目大意

  问在一个凸多边形内找一个点,连接这个点和所有顶点,使得与0号顶点,1号顶点构成的三角形是最小的概率。

  假设点的位置是$(x, y)$,那么可以用叉积来计算三角形的面积。

  这样可以列出$n - 1$个不等式。

  将每个化成形如$ax + by + c \leqslant 0$的形式。

  然后分类讨论($b = 0的时候需要特殊处理$)将它转换成二维平面上的半平面。

  接着做半平面交,算面积就好了。

Code

 /**
* bzoj
* Problem#4445
* Accepted
* Time: 288ms
* Memory: 20068k
*/
#include <algorithm>
#include <iostream>
#include <cassert>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef bool boolean; const double eps = 1e-;
#define check(_s) cerr << _s << endl; inline int dcmp(double x) {
if (fabs(x) <= eps) return ;
return (x < ) ? (-) : ();
} typedef class Vector {
public:
double x, y; Vector(double x = 0.0, double y = 0.0):x(x), y(y) { }
}Point, Vector; ostream& operator << (ostream& os, Point a) {
os << "(" << a.x << ", " << a.y << ")";
return os;
} Vector operator + (Vector a, Vector b) {
return Vector(a.x + b.x, a.y + b.y);
} Vector operator - (Vector a, Vector b) {
return Vector(a.x - b.x, a.y - b.y);
} Vector operator * (double x, Vector a) {
return Vector(a.x * x, a.y * x);
} double cross(Vector a, Vector b) {
return a.x * b.y - a.y * b.x;
} double dot(Vector a, Vector b) {
return a.x * b.x + a.y * b.y;
} Point getLineIntersection(Point A, Vector v, Point B, Vector u) {
double t = (cross(B - A, u) / cross(v, u));
return A + t * v;
} typedef class Line {
public:
Point p;
Vector v;
double ang; Line() { }
Line(Point p, Vector v):p(p), v(v) {
ang = atan2(v.y, v.x);
} boolean operator < (Line b) const {
// return dcmp(cross(v, b.v)) > 0;
return ang < b.ang;
}
}Line; const int N = 1e5 + ; int n;
int st = , ed = , top;
Line *ls, *qs;
Point *ps; inline void init() {
scanf("%d", &n);
ps = new Point[(n << ) + ];
for (int i = ; i < n; i++)
scanf("%lf%lf", &ps[i].x, &ps[i].y);
} inline void build() {
int d;
Point cur, nxt, vec;
ls = new Line[(n << ) + ];
vec = ps[] - ps[];
ls[++top] = Line(ps[], vec);
double bx = vec.y, by = vec.x, bc = cross(vec, ps[]), nx, ny, nc;
for (int i = ; i < n; i++) {
cur = ps[i], nxt = ps[(i + ) % n], vec = nxt - cur;
nx = bx - vec.y, ny = by - vec.x, nc = bc - cross(vec, cur);
d = dcmp(ny);
if (!d) {
d = dcmp(nx);
if (!d) continue;
ls[++top] = Line(Point(-nc / nx, ), Vector(, -d)); } else {
nx /= ny, nc /= ny;
ls[++top] = Line(Point(, nc), Vector(-d, -nx * d));
}
ls[++top] = Line(cur, vec);
}
} boolean isCrossingOut(Line b, Line cur, Line a) {
assert (dcmp(cross(cur.v, a.v)));
// if (!dcmp(cross(cur.v, a.v))) return true;
Point p = getLineIntersection(cur.p, cur.v, a.p, a.v);
return dcmp(cross(p - b.p, b.v)) > ;
} inline void HalfPlaneIntersection(int n) {
sort(ls + , ls + n + );
qs = new Line[n + ];
for (int i = ; i <= n; i++) {
while (st < ed && isCrossingOut(ls[i], qs[ed], qs[ed - ])) ed--;
while (st < ed && isCrossingOut(ls[i], qs[st], qs[st + ])) st++;
qs[++ed] = ls[i];
if (st < ed && !dcmp(cross(qs[ed].v, qs[ed - ].v))) {
ed--;
if (dcmp(cross(qs[ed].p - ls[i].v, ls[i].v)) < )
qs[ed] = ls[i];
}
}
while (st < ed && isCrossingOut(qs[st], qs[ed], qs[ed - ])) ed--;
} inline double PolygonArea(Point *ps, int n) {
double rt = cross(ps[n], ps[]);
for (int i = ; i < n; i++)
rt += cross(ps[i], ps[i + ]);
return fabs(rt) / ;
} inline void solve() {
double all = PolygonArea(ps - , n);
build();
HalfPlaneIntersection(top);
// assert(st < ed - 1);
if (st >= ed - ) {
puts("0.0000");
return;
}
for (int i = st; i < ed; i++)
ps[i] = getLineIntersection(qs[i].p, qs[i].v, qs[i + ].p, qs[i + ].v);
ps[ed] = getLineIntersection(qs[ed].p, qs[ed].v, qs[st].p, qs[st].v);
printf("%.4f", PolygonArea(ps + (st - ), ed - st + ) / all);
} int main() {
init();
solve();
return ;
}