【luogu P4250】【bzoj 4445】【LOJ 2008】【2022 省选训练赛 Contest 15 B】[SCOI2015]小凸想跑步 / convex(半平面交)
[SCOI2015]小凸想跑步 / convex
题目链接:luogu P4250 / bzoj 4445 / LOJ 2008 / 2022 省选训练赛 Contest 15 B
题目大意
给你一个凸多边形,然后你要在这个凸多边形里面选一个点,要这个点满足它跟每个凸多边形的端点连边,构成的若干个三角形中面积最小的是跟 0 号点 1 号点构成的三角形。
问你选的点合法的概率。
思路
我们其实会想到满足的点应该是构成了一个 \(0,1\) 号点为其中一个边缘的一个多边形。
然后那概率就是这个多边形的面积除以整个多边形的面积,那问题就是怎么找这个多边形。
我们考虑它和其它边的优劣关系,那列出若干个式子我们要它们都满足,那其实会想到可能是半平面交。
考虑列式子出来。
\(\overrightarrow{ab}\times\overrightarrow{ao}<\overrightarrow{cd}\times\overrightarrow{co}\)(\(o\) 点的 \(x,y\) 坐标直接用 \(x,y\) 表示)
\((x_b-x_a)(y-y_a)-(y_b-y_a)(x-x_a)<(x_d-x_c)(y-y_c)-(y_d-y_c)(x-x_c)\)
\((x_b-x_a)(y-y_a)+(y_d-y_c)(x-x_c)-(y_b-y_a)(x-x_a)-(x_d-x_c)(y-y_c)<0\)
\((y_a-y_b-y_c+y_d)x-(x_a-x_b-x_c+x_d)y+(y_bx_a-x_by_a+x_dy_c-y_dx_c)<0\)
设 \((y_a-y_b-y_c+y_d)=a,(x_a-x_b-x_c+x_d)=b,(y_bx_a-x_by_a+x_dy_c-y_dx_c)=c\)
\(ax-by+c<0\)
\(by<-ax-c\)
然后你会发现它这个是一条直线的一边,所以就可以愉快的用半平面交啦。
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define db long double
using namespace std;
const int N = 200000 + 100;
const db eps = 1e-14;
struct node {
db x, y;
}G[N], a[N];
node operator +(node x, node y) {
return (node){x.x + y.x, x.y + y.y};
}
node operator -(node x, node y) {
return (node){x.x - y.x, x.y - y.y};
}
db operator *(node x, node y) {
return x.x * y.x + x.y * y.y;
}
db operator ^(node x, node y) {
return x.x * y.y - x.y * y.x;
}
node operator *(node x, db y) {
return (node){x.x * y, x.y * y};
}
struct line {
node x, y;
db k;
line() {}
line (node xx, node yy) {
x = xx; y = yy;
k = atan2((y - x).y, (y - x).x);
}
}L[N << 1];
int n, tot, q[N];
db Area_clac(node *G, int l, int r) {
db re = 0;
for (int i = l + 1; i < r; i++)
re += (G[i] - G[l]) ^ (G[i + 1] - G[l]);
return fabs(re / 2);
}
bool cmp(line x, line y) {
if (fabs(x.k - y.k) > eps) return x.k < y.k;
return ((y.x - x.x) ^ (x.y - y.x)) > eps;//左边的放前面
}
node Meet(line a, line b) {//线段求交点
db k = ((b.y - b.x) ^ (a.x - b.x)) / ((a.y - a.x) ^ (b.y - b.x));
return a.x + (a.y - a.x) * k;
}
bool check(node x, line y) {
return ((x - y.x) ^ (y.y - x)) > eps;
}
db Halfmeet() {
//去重
int tmp = 1;
for (int i = 2; i <= tot; i++)
if (L[i].k != L[i - 1].k) L[++tmp] = L[i];
tot = tmp;
//半平面交
int l = 1, r = 0;
for (int i = 1; i <= tot; i++) {
while (l < r && check(G[r], L[i])) r--;
while (l < r && check(G[l + 1], L[i])) l++;
q[++r] = i;
if (l < r) G[r] = Meet(L[q[r - 1]], L[q[r]]);
}
while (l < r && check(G[r], L[q[l]])) r--;
while (l < r && check(G[l + 1], L[q[r]])) l++;
G[l] = Meet(L[q[r]], L[q[l]]);
return Area_clac(G, l, r);
}
int main() {
// freopen("read.txt", "r", stdin);
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%Lf %Lf", &G[i].x, &G[i].y);
G[n + 1] = G[1]; db sum = Area_clac(G, 1, n + 1);
for (int i = 1; i <= n; i++) {
L[++tot] = line(G[i], G[i + 1]);
}
for (int i = 2; i <= n; i++) {
db a = (G[1].y - G[2].y - G[i].y + G[i + 1].y);
db b = (G[2].x - G[1].x - G[i + 1].x + G[i].x);
db c = (G[2].y * G[1].x - G[2].x * G[1].y + G[i + 1].x * G[i].y - G[i + 1].y * G[i].x);
if (fabs(b) < eps) L[++tot] = line((node){-c / a, 0}, (node){-c / a - b, a});
else L[++tot] = line((node){0, -c / b}, (node){-b, -c / b + a});
}
sort(L + 1, L + tot + 1, cmp);
printf("%.4Lf", Halfmeet() / sum);
return 0;
}