内存限制:256 MiB 时间限制:1000 ms
问题描述
小凸晚上喜欢到操场跑步,今天他跑完两圈之后,他玩起了这样一个游戏。
操场是个凸 n 边形,N 个顶点按照逆时针从 0∼n−1 编号。现在小凸随机站在操场中的某个位置,标记为 P 点。将 P 点与 n个顶点各连一条边,形成 N 个三角形。如果这时 P 点, 0 号点,1 号点形成的三角形的面积是 N个三角形中最小的一个,小凸则认为这是一次正确站位。
现在小凸想知道他一次站位正确的概率是多少。
输入格式
第一行包含 1 个整数 n ,表示操场的顶点数和游戏的次数。 接下来有 N 行,每行包含两个整数 Xi 、Yi表示顶点的坐标。
输入保证按逆时针顺序输入点,所有点保证构成一个 n 多边形。所有点保证不存在三点共线。
输出格式
输出一个数,正确站位的概率,保留 4 位小数。
样例输入 1
5
1 8
0 7
0 0
8 0
8 8
样例输出 1
0.6316
样例输入 2
4
0 0
7 0
5 5
-1 4
样例输出 2
0.2475
提示
3≤N≤10^5,−10^9≤X,Y≤10^9
题解
仅凭直觉和想象很难确定满足条件的点的位置(至少鄙人是这样……),这时候就需要用数学式帮助解题。根据题意,我们用叉乘求面积的方法(给点用叉乘,给边用海伦公式)写出一系列不等式(其实一组通式就够了),化简之后得到一组二元一次不等式。
在信息学中,我们通常用最短路(差分约束系统)来求解不等式组,然而本题只有两个变元且都带有系数,于是不能用图论算法解决。回忆在数学课上,我们通过线性规划的方法解二元一次不等式组,然而线性规划的过程不就是在求一个半平面交吗?于是本题就用半平面交解决。
注意一个小细节:鄙人使用直线左侧表示半平面,因此最后化简出的不等式都应大于零。
代码
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
typedef double db;
const int maxn=1e5+5;
const db eps=1e-8;
int n,cnt,head,tail;
db area,ans;
struct Point{
db x,y;
Point(){}
Point(db x,db y):x(x),y(y){}
}p[maxn],temp[maxn];
typedef Point Vector;
struct Line{
Point P;
Vector v;
db ang;
Line(){}
Line(Point P,Vector v):P(P),v(v){ang=atan2(v.y,v.x);}
}L[2*maxn],q[2*maxn];
bool operator < (const Line& a,const Line& b)
{
return a.ang<b.ang;
}
Point operator + (const Point& a,const Vector& b)
{
return Point(a.x+b.x,a.y+b.y);
}
Vector operator - (const Point& a,const Point& b)
{
return Vector(a.x-b.x,a.y-b.y);
}
Vector operator * (const Vector& a,const db& b)
{
return Vector(a.x*b,a.y*b);
}
db Cross(Vector a,Vector b)
{
return a.x*b.y-a.y*b.x;
}
bool Onleft(Line a,Point b)
{
return Cross(a.v,b-a.P)>0;
}
Point GetIS(Line a,Line b)
{
Vector u=a.P-b.P;
db t=Cross(b.v,u)/Cross(a.v,b.v);
return a.P+a.v*t;
}
void HPIS()
{
sort(L+1,L+1+cnt);
q[head=tail=1]=L[1];
for(int i=2;i<=cnt;i++)
{
while(head<tail&&!Onleft(L[i],p[tail-1])) tail--;
while(head<tail&&!Onleft(L[i],p[head])) head++;
q[++tail]=L[i];
if(fabs(Cross(q[tail].v,q[tail-1].v))<eps)
{
tail--;
if(Onleft(q[tail],L[i].P)) q[tail]=L[i];
}
if(head<tail) p[tail-1]=GetIS(q[tail-1],q[tail]);
}
while(head<tail&&!Onleft(q[head],p[tail-1])) tail--;
p[tail]=GetIS(q[head],q[tail]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&temp[i].x,&temp[i].y);
if(i>1) L[++cnt]=Line(temp[i-1],temp[i]-temp[i-1]);
}
L[++cnt]=Line(temp[n],temp[1]-temp[n]),temp[++n]=temp[1];
db tx1=temp[2].x-temp[1].x,ty1=temp[2].y-temp[1].y;
for(int i=1;i<n;i++)
{
area+=Cross(temp[i],temp[i+1]);
if(i>1)
{
db tx=temp[i+1].x-temp[i].x,ty=temp[i+1].y-temp[i].y;
db A=ty-ty1,B=tx1-tx,C=ty1*temp[1].x+tx*temp[i].y-ty*temp[i].x-tx1*temp[1].y;
if(fabs(A)<eps) L[++cnt]=Line(Point(0,-C/B),Vector(-B,A));
else L[++cnt]=Line(Point(-C/A,0),Vector(-B,A));
}
}
HPIS();
for(int i=head;i<tail;i++) ans+=Cross(p[i],p[i+1]);
ans+=Cross(p[tail],p[head]);
printf("%.4lf\n",ans/area);
return 0;
}

博客介绍了SCOI2015比赛中的一道题目,涉及概率计算和几何问题。小凸在操场跑步时玩了一个游戏,需要找到随机站位使得形成最小面积三角形的概率,并提供了输入输出格式、样例及解题思路。解题方法涉及到数学、不等式组和半平面交的概念,适合于算法和竞赛编程爱好者阅读。
1354

被折叠的 条评论
为什么被折叠?



