【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;
}
posted @ 2022-03-17 19:47  あおいSakura  阅读(22)  评论(0编辑  收藏  举报