AtCoder 3868 Holes(Graham扫描法)

B - Holes


Time limit : 2sec / Memory limit : 256MB

Score : 600 points

Problem Statement

There are N holes in a two-dimensional plane. The coordinates of the i-th hole are (xi,yi).

Let R=10101010. Ringo performs the following operation:

  • Randomly choose a point from the interior of a circle of radius R centered at the origin, and put Snuke there. Snuke will move to the hole with the smallest Euclidean distance from the point, and fall into that hole. If there are multiple such holes, the hole with the smallest index will be chosen.

For every i (1iN), find the probability that Snuke falls into the i-th hole.

Here, the operation of randomly choosing a point from the interior of a circle of radius R is defined as follows:

  • Pick two real numbers x and y independently according to uniform distribution on [−R,R].
  • If x2+y2R2, the point (x,y) is chosen. Otherwise, repeat picking the real numbers x,y until the condition is met.

Constraints

  • 2N100
  • |xi|,|yi|106(1iN)
  • All given points are pairwise distinct.
  • All input values are integers.

Input

Input is given from Standard Input in the following format:

N
x1 y1
:
xN yN

Output

Print N real numbers. The i-th real number must represent the probability that Snuke falls into the i-th hole.

The output will be judged correct when, for all output values, the absolute or relative error is at most 10−5.


Sample Input 1

Copy
2
0 0
1 1

Sample Output 1

Copy
0.5
0.5

If Ringo put Snuke in the region x+y1, Snuke will fall into the first hole. The probability of this happening is very close to 0.5. Otherwise, Snuke will fall into the second hole, the probability of which happening is also very close to 0.5.


Sample Input 2

Copy
5
0 0
2 8
4 5
2 6
3 10

Sample Output 2

Copy
0.43160120892732328768
0.03480224363653196956
0.13880483535586193855
0.00000000000000000000
0.39479171208028279727

【思路】

由于给定范围可以视作无穷大,在所选的点的凸包的范围外的面积远大于凸包内,我们只需要考虑落在凸包上的点就好,因为在无穷大的区域里随机选点最近点是凸包内部点的概率无限趋近于0。因此先用Graham扫描法求凸包,注意保留落在边上的点,再求凸包各边指向外外部的法向量的夹角占2π的比例,最后按顺序输出即可。


【代码】

//************************************************************************
// File Name: F.cpp
// Author: Shili_Xu
// E-Mail: shili_xu@qq.com 
// Created Time: 2018年03月24日 星期六 22时32分59秒
//************************************************************************

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

const double EPS = 1e-8, PI = acos(-1);
const int MAXN = 105;

struct point {
	double x, y;
	int id;
	
	point() {}
	point(double _x, double _y, int _id = -1) : x(_x), y(_y), id(_id) {}
	bool operator<(const point &another) const;
};

struct ans {
	double val;
	int id;

	ans() {}
	bool operator<(const ans &another) const
	{
		return id < another.id;
	}
};

int tot, n;
point p[MAXN], s[MAXN], normal[MAXN];
ans an[MAXN];
bool visited[MAXN];

int sgn(double x)
{
	if (fabs(x) < EPS) return 0;
	return (x > 0 ? 1 : -1);
}

double det(const point &a1, const point &a2, const point &b1, const point &b2)
{
	return ((a2.x - a1.x) * (b2.y - b1.y) - (a2.y - a1.y) * (b2.x - b1.x));
}

double dot(const point &a1, const point &a2, const point &b1, const point &b2)
{
	return ((a2.x - a1.x) * (b2.x - b1.x) + (a2.y - a1.y) * (b2.y - b1.y));
}

double dist(const point &p1, const point &p2)
{
	return sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
}

bool point::operator<(const point &another) const
{
	if (sgn(det(p[0], *this, p[0], another)) == 0)
		return dist(p[0], *this) < dist(p[0], another);
	else
		return det(p[0], *this, p[0], another) > 0;
}

void graham()
{
	double min_y = p[0].y, min_x = p[0].x;
	int index = 0;
	for (int i = 1; i <= n - 1; i++) {
		if (p[i].y < min_y || (sgn(p[i].y - min_y) == 0 && p[i].x < min_x)) {
			min_y = p[i].y;
			min_x = p[i].x;
			index = i;
		}
	}
	swap(p[0], p[index]);
	sort(p + 1, p + n);
	s[0] = p[0]; s[1] = p[1];
	tot = 2;
	for (int i = 2; i <= n - 1; i++) {
		while (tot >= 2 && sgn(det(s[tot - 1], p[i], s[tot - 1], s[tot - 2]) < 0)) tot--;
		s[tot++] = p[i];
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%lf %lf", &p[i].x, &p[i].y);
		p[i].id = i;
	}
	graham();
	for (int i = 0; i < tot; i++) {
		if (i == 0) {
			normal[i].y = -(s[i].x - s[tot - 1].x);
			normal[i].x = s[i].y - s[tot - 1].y;
			normal[i].id = s[i].id;
		}
		else {
			normal[i].y = -(s[i].x - s[i - 1].x);
			normal[i].x = s[i].y - s[i - 1].y;
			normal[i].id = s[i].id;
		}
	}
	point zero(0, 0);
	for (int i = 0; i < tot; i++) {
		if (i == tot - 1) {
			an[i].val = acos(dot(zero, normal[i], zero, normal[0]) / dist(zero, normal[i]) / dist(zero, normal[0])) / PI / 2;
			an[i].id = normal[i].id;
		}
		else {
			an[i].val = acos(dot(zero, normal[i], zero, normal[i + 1]) / dist(zero, normal[i]) / dist(zero, normal[i + 1])) / PI / 2;
			an[i].id = normal[i].id;
		}
	}
	memset(visited, false, sizeof(visited));
	for (int i = 0; i < tot; i++) visited[s[i].id] = true;
	for (int i = 0; i < n; i++)
		if (!visited[i]) {
			an[tot].val = 0.0000000;
			an[tot++].id = i;
		}
	sort(an, an + n);
	for (int i = 0; i < n; i++) printf("%.8f\n", an[i].val);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值