从前的我看zzy那篇论文,真的是麻烦,后面发现其实半平面交其实是只比凸包难一点的东西。
在学习半平面交之前,你需要对向量计算几何有初步的认识,还有就是atan2(y,x)函数的运用。
atan2(y,x)函数见这篇博客:
https://blog.youkuaiyun.com/chen825919148/article/details/7582510
网上的这篇博客应该是比较易懂的:
https://blog.youkuaiyun.com/qq_40861916/article/details/83541403
下面开始讲我的总结。
首先你需要知道半平面交是什么东西。
半平面即为一个二维平面上,一条直线左边或右边的平面。
半平面交即求这些半平面的交。
如果有交的话,可能会是一个无线大的平面,所以半平面交的题一般有边界规定。
不然,有交的话,交一定是一个凸多边形,这很显然。
为了方便,我们把直线用点+向量表示,规定这个向量的左手向是代表的半平面。
算法流程:
把所有的半平面按几角排序。
注意几角相同的半平面只用保留最左边的。
此时我们维护一个双端队列,每次将一个半平面加进去。
加进去要把队列的左端和右端无用的半平面都弹掉。
如果有三个半平面a、b、c
- b、c在队列里
- b、c的交点在a的右边,显然b无用。
注意最后需要把开头和结尾的半平面当作重新加进去弹一遍。
因为一开始加入两个半平面的话,你不会去考虑这两个半平面会把后面加入的半平面弹掉。
做完后,如果队列里不足三个半平面,说明无交。
然后剩下的直线就是凸包上的直线,且按逆时针顺序,相邻两个求交就可以求出这个凸包。
裸题:
JZOJ 6093
Code:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i < B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define db long double
#define pp printf
using namespace std;
struct P {
db x, y;
P(db _x = 0, db _y = 0) { x = _x, y = _y;}
};
struct L {
P p, v;
L(){}
L(P _p, P _v) { p = _p, v = _v;}
};
P operator +(P a, P b) { return P(a.x + b.x, a.y + b.y);}
P operator -(P a, P b) { return P(a.x - b.x, a.y - b.y);}
P operator *(P a, db b) { return P(a.x * b, a.y * b);}
db operator ^(P a, P b) { return a.x * b.y - a.y * b.x;}
P jd(L a, L b) { return b.p + b.v * ((a.v ^ (a.p - b.p)) / (a.v ^ b.v));}
db qa(P a) { return atan2(a.y, a.x);}
bool onr(P a, L b) { return ((a - b.p) ^ b.v) > 0;}
const int N = 5e5 + 5;
int num, T, n;
P a[N];
struct nod {
int x; db z;
} b[N], d[N]; int b0, d0;
int cmp_b(nod a, nod b) {
return a.z < b.z;
}
L c[N * 3], cc[N * 3]; int c0;
void ins(int x, int y) {
if(x == y) return;
c[++ c0] = onr(a[1], L(a[x], a[y] - a[x])) ? L(a[y], a[x] - a[y]) : L(a[x], a[y] - a[x]);
}
const db eps = 1e-12;
int C[N * 3]; db J[N * 3];
int cmp(int a, int b) {
db A = J[a], B = J[b];
if(abs(A - B) < eps) return onr(c[b].p, c[a]);
return A < B;
}
L z[N * 3]; int st, en;
int pd(L a, L b, L c) {
return onr(jd(b, c), a);
}
void bd() {
P a = P(1e6, -1e6), b = P(1e6, 1e6), C = P(-1e6, 1e6), d = P(-1e6, -1e6);
c[++ c0] = L(a, b - a);
c[++ c0] = L(b, C - b);
c[++ c0] = L(C, d - C);
c[++ c0] = L(d, a - d);
}
P w[N * 3]; int w0;
db ans;
int gx;
int main() {
freopen("everdream.in", "r", stdin);
freopen("everdream.out", "w", stdout);
scanf("%d %d", &num, &T);
fo(ii, 1, T) {
scanf("%d", &n);
fo(i, 1, n) scanf("%Lf %Lf", &a[i].x, &a[i].y);
b0 = d0 = c0 = w0 = gx = 0;
bd();
fo(i, 2, n) {
b[++ b0].x = i;
b[b0].z = qa(a[i] - a[1]);
}
sort(b + 1, b + b0 + 1, cmp_b);
fo(i, 1, b0) ins(b[i == 1 ? b0 : i - 1].x, b[i].x);
fo(i, 2, b0) if(abs(b[i - 1].z - b[i].z) < eps) {
gx = 1; break;
}
if(gx) {
pp("0\n"); continue;
}
fo(i, 2, n) {
d[++ d0].x = i;
d[d0].z = qa(a[1] - a[i]);
}
sort(d + 1, d + d0 + 1, cmp_b);
int l = 0;
fo(i, 1, d0) {
while(l < b0 && b[l + 1].z <= d[i].z) l ++;
if(l && abs(b[l].z - d[i].z) < eps) { gx = 1; break;}
ins(b[l == 0 ? b0 : l].x, d[i].x);
ins(b[l == b0 ? 1 : l + 1].x, d[i].x);
}
if(gx) {
pp("0\n"); continue;
}
fo(i, 1, c0) C[i] = i, J[i] = qa(c[i].v), cc[i] = c[i];
sort(C + 1, C + c0 + 1, cmp);
int cnt = 0;
fo(i, 1, c0) if(i == 1 || abs(J[C[i]] - J[C[i - 1]]) > eps)
c[++ cnt] = cc[C[i]];
st = 1; en = 0;
fo(i, 1, cnt) {
while(en - st > 0 && pd(c[i], z[en], z[en - 1])) en --;
while(en - st > 0 && pd(c[i], z[st], z[st + 1])) st ++;
z[++ en] = c[i];
}
while(en - st > 0 && pd(z[st], z[en - 1], z[en])) en --;
while(en - st > 0 && pd(z[en], z[st], z[st + 1])) st ++;
ans = 0;
if(en - st > 1) {
fo(i, st, en) w[++ w0] = jd(z[i], z[i == en ? st : i + 1]);
fo(i, 2, w0 - 1) ans += (w[i] - w[1]) ^ (w[i + 1] - w[1]);
}
pp("%.10Lf\n", abs(ans) / 2);
}
}