半平面学习小记

从前的我看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);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值