【计算几何】【模板】半平面交

Luogu P4196

半平面交(S&I算法)

求多条直线左侧包围图形
  1. 逆时针确定每条直线(可用叉积求面积正负)
  2. 极角排序(将极角相同的,按左右排)
  3. 每个极角只保留一个最左边的
  4. 用双端队列维护:
    判断队尾和(队尾-1)的交点是否在新直线的右侧
    判断队首和(队首+1)的交点是否在新直线的右侧
  5. 先判交点个数, > 2 >2 >2才会有交,否则为0
  6. 叉积求面积
#include<bits/stdc++.h>
using namespace std;
const int N=10,M=505;
const double eps=1e-10;
struct Point{
	double x,y,k;
	Point(){}
	Point(double a_,double b_){x=a_,y=b_,k=atan2(y,x);}
	friend Point operator *(const Point a,const double b){
		return Point(a.x*b,a.y*b);
	}//---
	friend Point operator +(const Point a,const Point b){
		return Point(a.x+b.x,a.y+b.y);
	}
	friend Point operator -(const Point a,const Point b){
		return Point(a.x-b.x,a.y-b.y);
	}
	friend double operator *(const Point a,const Point b){
		return a.x*b.x+a.y*b.y;
	}
	friend double operator ^(const Point a,const Point b){
		return a.x*b.y-a.y*b.x;
	}
}d[M];int d_p;
struct Line{
	Point A,B;double k;
	Line(){}
	Line(Point a,Point b){A=a,B=b,k=atan2(B.y-A.y,B.x-A.x);}
	friend bool operator <(const Line a,const Line b){
		return (fabs(a.k-b.k)<=eps)?((a.B-a.A)^(b.B-a.A))>=eps:a.k<b.k;
	}
}t[M];
Point get(Point a,Point b){return Point(b.x-a.x,b.y-a.y);}
Point cross(Line a,Line b)//求交点
{
	double s1=(get(a.A,b.A)^get(a.A,b.B));
	double s2=(get(a.B,b.B)^get(a.B,b.A));
	return a.A+(get(a.A,a.B)*(s1/(s1+s2)));
}
bool rit(Line a,Line b,Line c)//a是否在b,c交点的右边
{
	Point d=cross(b,c);
	return ((a.B-a.A)^(d-a.A))<eps;
}
Line z[M];
int n,m;
int p1,p2,cnt;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&m);
		for(int j=1;j<=m;j++)
		{
			double x,y;
			scanf("%lf%lf",&x,&y);
			d[j]={x,y};
		}
		for(int j=1;j<=m;j++)
		t[++d_p]={d[j],d[j==m?1:j+1]};//此题保证为逆时针
	}
	n=d_p;
	sort(t+1,t+n+1);//极角排序
	for(int i=1;i<n;i++)
	{
		if(fabs(t[i].k-t[i+1].k)<=eps)continue;
		t[++cnt]=t[i];
	}
	t[++cnt]=t[n];//保留最左
	p1=1,p2=0;
	z[++p2]=t[1];z[++p2]=t[2];
	for(int i=3;i<=cnt;i++)
	{
		while(p1<p2&&rit(t[i],z[p2],z[p2-1]))p2--;
		while(p1<p2&&rit(t[i],z[p1],z[p1+1]))p1++;
		z[++p2]=t[i];
	}
	while(p1<p2&&rit(z[p1],z[p2],z[p2-1]))p2--;
	while(p1<p2&&rit(z[p2],z[p1],z[p1+1]))p1++;
	//双端队列维护
	double ans=0;
	for(int i=p1;i<=p2;i++)d[i]=cross(z[i],z[i==p2?p1:i+1]);
	if(p2-p1+1>2)//判是否为空集
	for(int i=p1;i<=p2;i++)ans+=(d[i]^d[i==p2?p1:i+1]);//面积
	printf("%.3lf",ans/2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值