增量法求半平面交

定义

给n条(有向)直线,一个点在它们的半平面交中当且仅当它在每条线的左侧。
用一条线把整个平面切开,只取左边这个叫半平面。
半平面交的形状是一个凸多边形。

求解

把所有边按照极角排序,用双端队列维护当前半平面交有哪些边,不断加边的时候弹出没有必要的边。

队尾的边没有必要当且仅当它和上一条边的交点在准备加入的边的右侧;同理,当队列中前两条边的交点在准备加入的边的右侧也要弹掉队首。

如果有多条边极角相同,我们只取最左边的边;如果有两条边平行反向,除非它们重合,不然它们不会在我们维护的队列中相邻。如果我们发现两条相邻的边平行,说明半平面交面积为\(0\),可以直接\(return\)

最后队尾会有一些边没用(因为我们只判了原来在队列里的边)。我们可以假装加入了队首的那条边,用上面的方法来判。

如果最后出现了只剩两条边的鬼畜情况,说明中途加入了一个完全不包含原有半平面交的半平面,需要特判。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mxn=32768;
const double inf=1e10,eps=1e-10;
struct nd{
	double x,y;
	nd operator-(const nd &a)const{
		return (nd){x-a.x,y-a.y};
	}
	double operator*(const nd &a)const{
		return x*a.y-a.x*y;
	}
	nd operator*(const double &a)const{
		return (nd){x*a,y*a};
	}
	bool operator<(const nd &a)const{
		bool fx=y>eps||(!y&&x>eps),fy=a.y>eps||(!a.y&&a.x>eps);
		if (fx^fy) return fx;
		return x*a.y-a.x*y>eps;
	}
};
struct ln{
	nd x,y;
	double operator*(const ln &a)const{
		return (y-x)*(a.y-a.x);
	}
	bool operator!=(const ln &a)const{
		return y-x<a.y-a.x||a.y-a.x<y-x;
	}
};

nd itsc(ln a,ln b){
	nd A=a.y-a.x,B=b.y-b.x,C=b.x-a.x;
	double t=(A*C)/(A*B);
	return b.x-B*t;
}
bool isleft(nd a,ln b){
	return (a-b.x)*(a-b.y)>eps;
}
bool operator<(const ln &a,const ln &b){
	if (a.y-a.x<b.y-b.x) return 1;
	if (b.y-b.x<a.y-a.x) return 0;
	return isleft(a.x,b);
}
namespace Half_Plane{
	nd qn[mxn];
	ln ql[mxn];
	bool solve(ln *a,int n,nd *b,int &m){
		sort(a+1,a+n+1);
		int hd=0,tl=0;
		ql[hd]=a[1];
		for (int i=2;i<=n;++i)
			if (a[i]!=a[i-1]){
				for (;hd<tl&&!isleft(qn[tl],a[i]);--tl);
				for (;hd<tl&&!isleft(qn[hd+1],a[i]);++hd);
				if (hd<tl&&!(ql[tl]*a[i])) return 0;
				ql[++tl]=a[i],qn[tl]=itsc(ql[tl],ql[tl-1]);
			}
		for (;hd<tl&&!isleft(qn[tl],ql[hd]);--tl);
		if (tl-hd<2) return 0;
		qn[hd]=itsc(ql[hd],ql[tl]);
		m=0;
		for (int i=hd;i<=tl;++i) b[++m]=qn[i];
		return 1;
	}
}
int n,m;
ln a[mxn];

nd b[mxn];
int main()
{
	scanf("%d",&n);
	for (int i=1,k;i<=n;++i){
		scanf("%d",&k);
		for (int j=1;j<=k;++j)
			scanf("%lf%lf",&b[j].x,&b[j].y);
		for (int j=1;j<=k;++j)
			a[++m]=(ln){b[j],b[j%k+1]};
	}
	if (!Half_Plane::solve(a,m,b,m)){
		puts("0.000");
		return 0;
	}
	double ans=0;
	for (int i=1;i<=m;++i) ans+=b[i]*b[i%m+1];
	printf("%.3lf\n",ans/2+eps);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/zzqtxdy/p/12620461.html