[BZOJ4445][Scoi2015]小凸想跑步(半平面交)

Description

小凸晚上喜欢到操场跑步,今天他跑完两圈之后,他玩起了这样一个游戏。
操场是个凸n边形,N个顶点按照逆时针从0~n-l编号。现在小凸随机站在操场中的某个位置,标记为
P点。将P点与n个顶点各连一条边,形成N个三角形。如果这时P点,0号点,1号点形成的三角形的面
积是N个三角形中最小的一个,小凸则认为这是一次正确站位。
现在小凸想知道他一次站位正确的概率是多少。

Input

第1行包含1个整数n,表示操场的顶点数和游戏的次数。
接下来有N行,每行包含2个整数Xi,Yi表示顶点的坐标。
输入保证按逆时针顺序输入点,所有点保证构成一个n多边形。所有点保证不存在三点共线。

Output

输出1个数,正确站位的概率,保留4位小数。
Sample Input

5

1 8

0 7

0 0

8 0

8 8
Sample Output

0.6316

HINT

3<=N<=10^5,-10^9<=X,Y<=10^9

Solution

比较麻烦的一道题。
根据叉积的公式,可以知道,点 (x,y)(Xi,Yi)(Xj,Yj) ( x , y ) ( X i , Y i ) ( X j , Y j ) (x,y) ( x , y ) 在凸多边形内,且 j=(i+1)modn j = ( i + 1 ) mod n )构成的三角形面积为:

(Xix)(Yjy)(Xjx)(Yiy) ( X i − x ) ( Y j − y ) − ( X j − x ) ( Y i − y )

展开式子得到:
XiYjXiyYjxXjYi+Xjy+Yix X i Y j − X i y − Y j x − X j Y i + X j y + Y i x

而题意就是要求站位 (x,y) ( x , y ) 对于每个 i[1,n1],j=(i+1)modn i ∈ [ 1 , n − 1 ] , j = ( i + 1 ) mod n ,都有:
X0Y1X0yY1xX1Y0+X1y+Y0xXiYjXiyYjxXjYi+Xjy+Yix X 0 Y 1 − X 0 y − Y 1 x − X 1 Y 0 + X 1 y + Y 0 x ≤ X i Y j − X i y − Y j x − X j Y i + X j y + Y i x

实际上就是半平面:
(YjYi+Y0Y1)x+(XiXj+X1X0)y ( Y j − Y i + Y 0 − Y 1 ) x + ( X i − X j + X 1 − X 0 ) y

+(X0Y1X1Y0+XjYiXiYj)0 + ( X 0 Y 1 − X 1 Y 0 + X j Y i − X i Y j ) ≤ 0

一共有 n1 n − 1 个限制。
再附加一个: (x,y) ( x , y ) 必须在向量 (X0,Y0)(X1,Y1) ( X 0 , Y 0 ) ( X 1 , Y 1 ) → 的左侧,这样就保证了 (x,y) ( x , y ) 一定在多边形内。
这样,答案就是:
可 行 域 面 积 原 多 边 形 面 积

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5;
int n, tot, tot2;
struct cyx {
    double x, y;
    friend inline cyx operator + (cyx a, cyx b) {
        return (cyx) {a.x + b.x, a.y + b.y};
    }
    friend inline cyx operator - (cyx a, cyx b) {
        return (cyx) {b.x - a.x, b.y - a.y};
    }
    friend inline cyx operator * (cyx a, double b) {
        return (cyx) {a.x * b, a.y * b};
    }
    friend inline cyx operator / (cyx a, double b) {
        return (cyx) {a.x / b, a.y / b};
    }
    friend inline double operator * (cyx a, cyx b) {
        return a.x * b.y - a.y * b.x;
    }
} oz[N], otz[N];
struct pyz {cyx a, b; cyx o() {return a - b;}} b[N], a[N], Q[N];
double dist(cyx a, cyx b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
cyx orz(pyz x, pyz y) {
    double s1 = (x.a - y.a) * (x.a - y.b), s2 = (x.b - y.b) * (x.b - y.a);
    if (s1 + s2 == 0) return x.a; return x.a + x.o() * s1 / (s1 + s2);
}
bool check1(pyz x, pyz y) {
    double tmp = x.o() * y.o();
    return tmp > 0 || (tmp == 0 && y.o() * (y.a - x.a) > 0);
}
bool para(pyz x, pyz y) {return x.o() * y.o() == 0;}
bool comp(pyz x, pyz y) {
    cyx p = x.o(), q = y.o(); if ((p.y < 0 || (p.y == 0 && p.x < 0))
        && (q.y > 0 || (q.y == 0 && q.x > 0))) return 1;
    if ((p.y > 0 || (p.y == 0 && p.x > 0))
        && (q.y < 0 || (q.y == 0 && q.x < 0))) return 0; return check1(x, y);
}
bool check2(pyz a, pyz b, pyz x) {
    cyx y = orz(a, b); return (x.a - y) * x.o() >= 0;
}
double solve() {
    int i, H = 1, T = 2; sort(b + 1, b + n + 1, comp);
    For (i, 1, n) if (i == 1 || !para(b[i], b[i - 1])) a[++tot] = b[i];
    Q[1] = a[1]; Q[2] = a[2]; For (i, 3, tot) {
        while (H < T && check2(Q[T - 1], Q[T], a[i])) T--;
        while (H < T && check2(Q[H], Q[H + 1], a[i])) H++; Q[++T] = a[i];
    }
    while (H < T && check2(Q[T - 1], Q[T], Q[H])) T--;
    For (i, H, T - 1) otz[++tot2] = orz(Q[i], Q[i + 1]);
    otz[++tot2] = orz(Q[T], Q[H]); double ans = otz[1] * otz[tot2];
    For (i, 1, tot2 - 1) ans += otz[i + 1] * otz[i]; return abs(ans);
}
int main() {
    int i; n = read(); For (i, 1, n) oz[i].x = read(), oz[i].y = read();
    double area = oz[1] * oz[n]; For (i, 1, n - 1) area += oz[i + 1] * oz[i];
    area = abs(area); b[1] = (pyz) {oz[1], oz[2]}; For (i, 2, n) {
        int j = i == n ? 1 : i + 1;
        double A = oz[j].y - oz[i].y + oz[1].y - oz[2].y,
            B = oz[i].x - oz[j].x - oz[1].x + oz[2].x,
            C = oz[1].x * oz[2].y - oz[2].x * oz[1].y
            + oz[j].x * oz[i].y - oz[i].x * oz[j].y;
        if (B != 0) {
            A /= -B; C /= -B; cyx a = (cyx) {-i, -A * i + C},
                k = (cyx) {i, A * i + C};
            if (B > 0) b[i] = (pyz) {k, a}; else b[i] = (pyz) {a, k};
        }
        else {
            C /= -A; cyx a = (cyx) {C, -i}, k = (cyx) {C, i + 1};
            if (A > 0) b[i] = (pyz) {a, k}; else b[i] = (pyz) {k, a};
        }
    }
    printf("%.4lf\n", solve() / area);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值