【洛谷P4196】[CQOI2006]凸多边形 /【模板】半平面交

题目

https://www.luogu.com.cn/problem/P4196

思路

求半平面交:

  1. 先极角排序,如果极角相同,保留最左边那一个
  2. 维护一个双端队列,考虑加入一个半平面:
    1. while deque顶端的两个半平面的交点在当前半平面外:删除deque顶端的半平面。

    2. while deque底部的两个半平面的交点在当前半平面外:删除deque底部的半平面。

    3. 将新半平面加入deque顶端。

  3. 去除多余的半平面:
    1. while deque顶端的两个半平面的交点在底部半平面外:删除deque顶端的半平面。

    2. while deque底部的两个半平面的交点在顶端半平面外:删除deque底部的半平面。

    3. 重复上述两条直到不能删除为止。

后面根据交点求面积就行

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1077;
const double eps=1e-10;
struct node
{
	double x,y;
	node(double _x=0,double _y=0):x(_x),y(_y){}
	node operator + (const node &a) const {return node(x+a.x,y+a.y);}
	node operator - (const node &a) const {return node(x-a.x,y-a.y);}
	void operator += (const node &a) {this->x+=a.x,this->y+=a.y;}
	void operator -= (const node &a) {this->x-=a.x,this->y-=a.y;}
	node operator * (const double &a) const {return node(x*a,y*a);}
	node operator / (const double &a) const {return node(x/a,y/a);}
	void operator *= (const double &a) {this->x*=a,this->y*=a;}
	void operator /= (const double &a) {this->x/=a,this->y/=a;}
}Q[N],P[N];
double cp(node A,node B){return A.x*B.y-A.y*B.x;}
double pp(node A,node B){return A.x*B.x+A.y*B.y;}
struct line
{
	node a,b; double k;
	line(node _a,node _b):a(_a),b(_b){k=atan2(b.y,b.x);};
	line(){};
	bool operator < (const line &t)const {return k<t.k;}
}yjy[N],aii[N];
node get(line A,line B)
{
	node C=A.a-B.a;
	double t=cp(B.b,C)/cp(A.b,B.b);
	return A.a+A.b*t;
}
int L=1,R=1,n,tot;
void work()
{
	aii[L]=yjy[1];
	for(int i=2; i<=tot; i++)
	{
		while(L<R&&cp(yjy[i].b,P[R-1]-yjy[i].a)<=eps) --R;
		while(L<R&&cp(yjy[i].b,P[L]-yjy[i].a)<=eps) ++L;
		aii[++R]=yjy[i];
		if(fabs(cp(aii[R].b,aii[R-1].b))<=eps)
		{
			R--;
			if(cp(aii[R].b,yjy[i].a-aii[R].a)>eps) aii[R]=yjy[i];
		}
		if(L<R) P[R-1]=get(aii[R-1],aii[R]);
	}
	while(L<R&&cp(aii[L].b,P[R-1]-aii[L].a)<=eps) R--;
	if(R-L<=1) return;
	P[R]=get(aii[L],aii[R]);
}
void solve()
{
	double ans=0;
	for(int i=L; i<=R; i++) if(i==R) ans+=cp(P[i],P[L]);else ans+=cp(P[i],P[i+1]);
	printf("%.3lf",ans/2);
}
int main()
{
	scanf("%d",&n);
	for(int i=1; i<=n; i++)
	{
		int m; scanf("%d",&m);
		for(int j=1; j<=m; j++) scanf("%lf%lf",&Q[j].x,&Q[j].y);
		for(int j=1; j<=m; j++)
		{
			
			tot++;
			if(j==m) yjy[tot]=line(Q[j],Q[1]-Q[j]);
			else yjy[tot]=line(Q[j],Q[j+1]-Q[j]);
		}
	}
	sort(yjy+1,yjy+tot+1);
	work(); solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值