【SCOI2015】小凸想跑步

内存限制: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
#include
#include
#include
#include
#include
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.angreturn 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(head1])) tail--;
        while(headq[++tail]=L[i];
        if(fabs(Cross(q[tail].v,q[tail-1].v))if(Onleft(q[tail],L[i].P)) q[tail]=L[i];
        }
        if(head1]=GetIS(q[tail-1],q[tail]);
    }
    while(headq[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;i1]);
        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)0,-C/B),Vector(-B,A));
            else L[++cnt]=Line(Point(-C/A,0),Vector(-B,A));
        }
    }
    HPIS();
    for(int i=head;i1]);
    ans+=Cross(p[tail],p[head]);
    printf("%.4lf\n",ans/area);
    return 0;
}

你可能感兴趣的:(计算几何,--------半平面交)