洛谷 P4250 [SCOI2015]小凸想跑步(最后的半平面交)

Problem

就在这里

Solution

概率就是正确站位面积除以凸包面积。

离第一条边比第 k(k>1) 条边更近的充要条件是

(x1x)(y2y)(y1y)(x2x)(xkx)(yk+1y)(yky)(xk+1x)

化简发现它是一条直线,其中
A=y1y2yk+yk+1
B=x2x1xk+1+xk
C=x1y2x2y1xkyk+1+xk+1yk

然后就上半平面交,求出可行域的面积即可。为了保证在凸包内,将其边也加进去。记得特判 B=0 的情况。

半平面交要求直线交点,我每次求的都是线段的,错了好多次(输出nan让我感到无力。。)。改成另一种适用于直线的写法即可(参考代码,为了证明我甚至用到了全等)。

最后关于有向直线的方向的判定,可以找几个特例来快速判断。

Code

#include 
#define Eps 1e-10
#define maxn 200010

using namespace std;

int nn, n, cnt;

struct Point{
    double x, y;
    Point() {}
    Point(double _x, double _y):x(_x), y(_y) {}
    friend Point operator + (Point A, Point B){return Point(A.x + B.x, A.y + B.y);}
    friend Point operator - (Point A, Point B){return Point(A.x - B.x, A.y - B.y);}
    friend Point operator * (double A, Point B){return Point(A * B.x, A * B.y);}
    friend Point operator / (Point A, double B){return Point(A.x / B, A.y / B);}
    friend double Dot(Point A, Point B){return A.x * B.x + A.y * B.y;}
    friend double Det(Point A, Point B){return A.x * B.y - A.y * B.x;}
}Poly[maxn], p[maxn], o[maxn];

struct Line{
    Point P, v;
    double ang;
    Line() {}
    Line(Point _P, Point _v):P(_P), v(_v) {ang = atan2(v.y, v.x);}
    bool operator < (const Line& l) const{return ang < l.ang;}
}temp, L[maxn], q[maxn];

bool OnLeft(Point A, Line B){
    return Det(A - B.P, B.v) < -Eps;
}

Point Cross(Line A, Line B){
    Point u = A.P - B.P;
    double t = Det(B.v, u) / Det(A.v, B.v);
    return A.P + t * A.v;
}//直线相交

bool HPI(){

    sort(L+1, L+n+1);
    int hh = 0, tt = 0;
    q[hh] = L[1];

    for(int i = 2; i <= n; i++){
        while(hh < tt && !OnLeft(p[tt-1], L[i]))  tt --;
        while(hh < tt && !OnLeft(p[hh], L[i]))  hh ++;
        q[++tt] = L[i];
        if(fabs(Det(q[tt].v, q[tt-1].v)) < Eps){
            tt --;
            if(OnLeft(L[i].P, q[tt]))  q[tt] = L[i];
        }
        if(hh < tt)  p[tt-1] = Cross(q[tt-1], q[tt]);
    }
    while(hh < tt && !OnLeft(p[tt-1], q[hh]))  tt --;
    if(tt - hh <= 1)  return false;
    p[tt] = Cross(q[tt], q[hh]);

    cnt = 0;
    for(int i = hh; i <= tt; i++)  Poly[++cnt] = p[i];
    Poly[cnt+1] = Poly[1];
    return true;
}

double GetS(Point *Trista, int m){
    double S = 0.0;
    for(int i = 1; i <= m; i++)
        S += Det(Trista[i], Trista[i+1]);
    return S;
}

int main(){

    scanf("%d", &nn);

    for(int i = 1; i <= nn; i++)  scanf("%lf%lf", &o[i].x, &o[i].y);

    o[nn+1] = o[1];

    for(int i = 1; i <= nn; i++){
        L[++n] = Line(o[i], Point(o[i+1] - o[i]));
        if(i == 1)  continue;
        double A = o[1].y - o[2].y - o[i].y + o[i+1].y;
        double B = o[2].x - o[1].x + o[i].x - o[i+1].x;
        double C = Det(o[1], o[2]) - Det(o[i], o[i+1]);
        if(fabs(B) < Eps)  temp = Line(Point(-C/A, 0), Point(-B, A));
        else  temp = Line(Point(0, -C/B), Point(-B, A));
        L[++n] = temp;
    }

    if(HPI()){
        double S1 = GetS(o, nn);
        double S2 = GetS(Poly, cnt);
        printf("%.4lf\n", S2 / S1);
    }

    else  printf("0.0000\n");

    return 0;
}

这里写图片描述

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