[Luogu P4250] [BZOJ 4445] [SCOI2015]小凸想跑步

洛谷传送门
BZOJ传送门

题目描述

小凸晚上喜欢到操场跑步,今天他跑完两圈之后,他玩起了这样一个游戏。

操场是个凸 n n n 边形, n n n 个顶点按照逆时针从 0 ∼ n − 1 0 \sim n - 1 0n1 编号。现在小凸随机站在操场中的某个位置,标记为 p p p点。将 p p p 点与 n n n 个顶点各连一条边,形成 n n n 个三角形。如果这时 p p p 点, 0 0 0 号点, 1 1 1 号点形成的三角形的面 积是 n n n 个三角形中最小的一个,小凸则认为这是一次正确站位。

现在小凸想知道他一次站位正确的概率是多少。

输入输出格式

输入格式:

1 1 1 行包含 1 1 1 个整数 n n n, 表示操场的顶点数和游戏的次数。

接下来有 n n n 行,每行包含 2 2 2 个整数 x i , y i x_i, y_i xi,yi,表示顶点的坐标。

输入保证按逆时针顺序输入点,所有点保证构成一个凸多边形。所有点保证不存在三点共线。

输出格式:

输出 1 1 1 个数,正确站位的概率,保留 4 4 4 位小数。

输入输出样例

输入样例#1:
5
1 8
0 7
0 0
8 0
8 8
输出样例#1:
0.6316

说明

对于 30 % 30\% 30% 的数据, 3 ≤ n ≤ 4 , 0 ≤ x , y ≤ 10 3 \leq n \leq 4, 0 \leq x, y \leq 10 3n4,0x,y10

对于 100 % 100\% 100% 的数据, 3 ≤ n ≤ 1 0 5 , − 1 0 9 ≤ x , y ≤ 1 0 9 3 \leq n \leq 10^5, -10^9 \leq x, y \leq 10^9 3n105,109x,y109

解题分析

设一条边两端的两个点为 A i ( x i , y i ) , A j ( x j , y j ) A_i(x_i,y_i),A_j(x_j,y_j) Ai(xi,yi),Aj(xj,yj), 那么面积为
( x j − x i ) ( y p − y i ) − ( y j − y i ) ( x p − x i ) = x p ( y i − y j ) + y p ( x j − x i ) − x j y i + x i y j (x_j-x_i)(y_p-y_i)-(y_j-y_i)(x_p-x_i) \\ =x_p(y_i-y_j)+y_p(x_j-x_i)-x_jy_i+x_iy_j (xjxi)(ypyi)(yjyi)(xpxi)=xp(yiyj)+yp(xjxi)xjyi+xiyj
再加上 S △ A 0 A 1 A p &lt; S △ A i A j A p S\triangle A_0A_1A_p&lt;S\triangle A_iA_jA_p SA0A1Ap<SAiAjAp这个限制, 可得:
x p ( y i − y j − y 0 + y 1 ) + y p ( x j − x i − x 1 + x 0 ) + ( x i y j − x j y i − x 0 y 1 + x 1 y 0 ) &gt; 0 x_p(y_i-y_j-y_0+y_1)+y_p(x_j-x_i-x_1+x_0)+(x_iy_j-x_jy_i-x_0y_1+x_1y_0)&gt; 0 xp(yiyjy0+y1)+yp(xjxix1+x0)+(xiyjxjyix0y1+x1y0)>0
然后每个不等式都是一个半平面的形式, 搞搞半平面交就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define EPS 1e-8
#define db long double
#define MX 200500
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
template <class T> IN T abs(T a) {return a > 0 ? a : -a;}
int n, lcnt, h, t;
db all, area;
struct Point {db x, y;} dat[MX], inter[MX];
IN Point operator + (const Point &x, const Point &y) {return {x.x + y.x, x.y + y.y};}
IN Point operator - (const Point &x, const Point &y) {return {x.x - y.x, x.y - y.y};}
IN Point operator * (R db k, const Point &x) {return {x.x * k, x.y * k};}
IN db Getk(const Point &x) {return std::atan2(x.y, x.x);}
IN db operator * (const Point &x, const Point &y) {return x.x * y.y - x.y * y.x;}
struct Line {Point fr, to; db rat;} line[MX], que[MX];
IN bool operator < (const Line &x, const Line &y) {return x.rat < y.rat;}
IN Point GetInt(const Line &x, const Line &y)
{
	db k1 = (y.to - y.fr) * (x.to - y.fr);
	db k2 = (y.to - y.fr) * (x.fr - y.fr);
	return x.fr + (-k2 / (k1 - k2)) * (x.to - x.fr);
}
IN bool onleft(const Line &x, const Point &y)
{return (x.to - x.fr) * (y - x.fr) > EPS;}
IN int sig (R db x) {return (x > -EPS) - (x < EPS);}
IN db Getk(const Line &x) {return Getk(x.to - x.fr);}
int main(void)
{
	db a, b, c;
	Point fr, to;
	scanf("%d", &n);
	for (R int i = 0; i < n; ++i) scanf("%Lf%Lf", &dat[i].x, &dat[i].y);
	dat[n] = dat[0];
	for (R int i = 2; i < n; ++i) all += abs((dat[i - 1] - dat[0]) * (dat[i] - dat[0]) / 2);
	for (R int i = 1; i <= n; ++i) line[++lcnt] = {dat[i - 1], dat[i]};
	for (R int i = 1; i < n; ++i)
	{
		a = dat[i].y - dat[i + 1].y - dat[0].y + dat[1].y;
		b = dat[i + 1].x - dat[i].x - dat[1].x + dat[0].x;
		c = dat[i].x * dat[i + 1].y - dat[i + 1].x * dat[i].y - dat[0].x * dat[1].y + dat[1].x * dat[0].y;
		if (!sig(a))
		{
			db height = -c / b;
			fr = {0, height}, to = {1, height};
			if (b > 0) line[++lcnt] = {fr, to};
			else line[++lcnt] = {to, fr};
		}
		else if (!sig(b))
		{
			db wid = -c / a;
			fr = {wid, 0}, to = {wid, 1};
			if (a > 0) line[++lcnt] = {to, fr};
			else line[++lcnt] = {fr, to};
		}
		else
		{
			db frx, fry, tox, toy;
			frx = 0, fry = -c / b, fr = {frx, fry};
			tox = 1, toy = (-c - a) / b, to = {tox, toy};
			if (a + (toy - 1) * b > -c) line[++lcnt] = {to, fr};
			else line[++lcnt] = {fr, to};
		}
	}
	for (R int i = 1; i <= lcnt; ++i) line[i].rat = Getk(line[i]);
	std::sort(line + 1, line + 1 + lcnt);
	que[h = t = 0] = line[1];
	for (R int i = 2; i <= lcnt; ++i)
	{
		W (h < t && !onleft(line[i], inter[t - 1])) --t;
		W (h < t && !onleft(line[i], inter[h])) ++h;
		que[++t] = line[i];
		if (!sig((line[i].to - line[i].fr) * (que[t - 1].to - que[t - 1].fr)))
		{
			--t;
			if (onleft(que[t], line[i].fr)) que[t] = line[i];
		}
		if (h < t) inter[t - 1] = GetInt(que[t], que[t - 1]);
	}
	W (h < t && !onleft(que[h], inter[t - 1])) --t;
	if (t - h <= 1) return puts("0.0000"), 0;
	inter[t] = GetInt(que[t], que[h]);
	for (R int i = h + 2; i <= t; ++i) area += abs((inter[i - 1] - inter[h]) * (inter[i] - inter[h]) / 2);
	printf("%.4Lf", area / all);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值