BZOJ 4445 [Scoi2015]小凸想跑步:半平面交

Leohh發表於2018-06-18

傳送門

題意

小凸晚上喜歡到操場跑步,今天他跑完兩圈之後,他玩起了這樣一個遊戲。

操場是個凸 $ n $ 邊形,$ n $ 個頂點 $ P_i $ 按照逆時針從 $ 0 $ 至 $ n-1 $ 編號。

現在小凸隨機站在操場中的某個位置,標記為 $ P $ 點。將 $ P $ 點與 $ n $ 個頂點各連一條邊,形成 $ n $ 個三角形。如果這時 $ (P, P_0, P_1) $ 形成的三角形的面積是 $ n $ 個三角形中最小的一個,小凸則認為這是一次正確站位。

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

題解

對於一次正確站位 $ P $ 來說,要滿足兩個條件:

  1. $ area(P, P_0. P_1) < area(P, P_i, P_{i+1}) \quad (1 \leq i \lt n-1) $,其中 $ area $ 表示三角形面積。
  2. $ P $ 在多邊形內部。

對於條件1來說,將面積轉化成叉積形式:
\[ \overrightarrow{PP_0} \times \overrightarrow{PP_1} < \overrightarrow{PP_i} \times \overrightarrow{PP_{i+1}} \quad (1 \leq i \lt n-1) \]
然後再將向量拆開,整理得:
\[ (-y_1+y_0+y_{i+1}-y_i)x + (-x_0+x_1+x_i-x_{i+1})y + (x_0y_1-x_1y_0-x_iy_{i+1}+x_{i+1}y_i) < 0 \]
這樣就得到了 $ n $ 個以一般式 $ Ax+By+C<0 $ 的形式表示的半平面。

另外對於條件2來說,也是 $ n-1 $ 個半平面。

所以總共就有了 $ 2n-1 $ 個半平面,跑一邊半平面交,就求出了正確站位的總面積 $ S_{right} $ 。

設凸多邊形的面積為 $ S $ ,則答案就是 $ \dfrac{S_{right}}{S} $ 。

AC Code

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define MAX_N 200005
#define EPS 1e-10
#define eq(x,y) (fabs((x)-(y))<EPS)

using namespace std;

struct Coor
{
    double x,y;
    Coor(double _x,double _y) { x=_x,y=_y; }
    Coor(){}
    friend Coor operator + (const Coor &a,const Coor &b)
    {
        return Coor(a.x+b.x,a.y+b.y);
    }
    friend Coor operator - (const Coor &a,const Coor &b)
    {
        return Coor(a.x-b.x,a.y-b.y);
    }
    friend Coor operator * (const Coor &a,double b)
    {
        return Coor(a.x*b,a.y*b);
    }
    friend Coor operator / (const Coor &a,double b)
    {
        return Coor(a.x/b,a.y/b);
    }
    friend double len(Coor a,Coor b)
    {
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    friend double dot(Coor a,Coor b)
    {
        return a.x*b.x+a.y*b.y;
    }
    friend double cross(Coor a,Coor b)
    {
        return a.x*b.y-a.y*b.x;
    }
    friend double area(Coor a,Coor b,Coor c)
    {
        return fabs(cross(b-a,c-a))/2.0;
    }
};

struct Line
{
    Coor a,b;
    double s;
    Line(Coor _a,Coor _b)
    {
        a=_a,b=_b;
        s=atan2(b.y-a.y,b.x-a.x);
    }
    Line(){}
    friend bool operator < (const Line &l1,const Line &l2)
    {
        return l1.s!=l2.s ? l1.s<l2.s : cross(l1.b-l1.a,l2.b-l1.a)<0;
    }
    friend Coor inter(Line l1,Line l2)
    {
        Coor x=l1.b-l1.a,y=l2.b-l2.a,u=l1.a-l2.a;
        Coor ans=l1.a+x*(cross(y,u)/cross(x,y));
        return ans;
    }
    friend bool onlef(Coor p,Line l)
    {
        return cross(l.b-l.a,p-l.b)>0;
    }
};

int n,tot=0,cnt=0;
double sum=0,ans=0;
Coor p[MAX_N];
Coor a[MAX_N];
Line l[MAX_N];
Line q[MAX_N];

void read()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
    p[n]=p[0];
    for(int i=1;i<n-1;i++) sum+=area(p[0],p[i],p[i+1]);
}

void build()
{
    for(int i=1;i<n;i++)
    {
        double a=-p[1].y+p[0].y+p[i+1].y-p[i].y;
        double b=-p[0].x+p[1].x+p[i].x-p[i+1].x;
        double c=p[0].x*p[1].y-p[1].x*p[0].y-p[i].x*p[i+1].y+p[i+1].x*p[i].y;
        Coor u=(eq(b,0) ? Coor(-c/a,0) : Coor(0,-c/b)),v(-b,a);
        l[++tot]=Line(u,u+v);
    }
    for(int i=0;i<n;i++) l[++tot]=Line(p[i],p[i+1]);
}

void hpi()
{
    sort(l+1,l+1+tot);
    int L=1,R=0,now=0;
    for(int i=1;i<=tot;i++) if(i==1 || l[i].s!=l[now].s) l[++now]=l[i];
    tot=now,q[++R]=l[1],q[++R]=l[2];
    for(int i=3;i<=tot;i++)
    {
        while(L<R && !onlef(inter(q[R],q[R-1]),l[i])) R--;
        while(L<R && !onlef(inter(q[L],q[L+1]),l[i])) L++;
        q[++R]=l[i];
    }
    while(L<R && !onlef(inter(q[R],q[R-1]),q[L])) R--;
    while(L<R && !onlef(inter(q[L],q[L+1]),q[R])) L++;
    q[R+1]=q[L];
    for(int i=L;i<=R;i++) a[++cnt]=inter(q[i],q[i+1]);
    for(int i=2;i<cnt;i++) ans+=area(a[1],a[i],a[i+1]);
}

void work()
{
    build();
    hpi();
    printf("%.4f\n",ans/sum);
}

int main()
{
    read();
    work();
}

相關文章