2803. 凸多边形

题目链接

2803. 凸多边形

逆时针给出 \(n\) 个凸多边形的顶点坐标,求它们交的面积。

例如 \(n=2\) 时,两个凸多边形如下图:

image

则相交部分的面积为 \(5.233\)

输入格式

第一行有一个整数 \(n\),表示凸多边形的个数,以下依次描述各个多边形。

\(i\) 个多边形的第一行包含一个整数 \(m_i\),表示多边形的边数,以下 \(m_i\) 行每行两个整数,逆时针给出各个顶点的坐标。

输出格式

仅包含一个实数,表示相交部分的面积,保留三位小数。

数据范围

\(2 \le n \le 10\),
\(3 \le m_i \le 50\),
每维坐标为 \([-1000,1000]\) 内的整数。

输入样例:

2
6
-2 0
-1 -2
1 -2
2 0
1 2
-1 2
4
0 -3
1 -1
2 2
-1 0

输出样例:

5.233

解题思路

半平面交

先将所有直线极角排序,用栈存储最后保存的直线,对于当前直线,如果其在前两个直线交点的左边,则显然不行,需要出栈,最后要闭合需要判断首尾两条边

  • 时间复杂度:\(O(nlogn)\)

代码

// Problem: 凸多边形
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2805/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=505;
const double eps=1e-8;
typedef pair<double,double> PDD;
int n,m,cnt,q[N];
PDD a[N],ans[N];
struct Line
{
	PDD st,en;
}line[N];
PDD operator-(PDD a,PDD b)
{
	return {a.fi-b.fi,a.se-b.se};
}
int sign(double x)
{
	if(fabs(x)<eps)return 0;
	if(x<0)return -1;
	return 1;
}
int dcmp(double x,double y)
{
	if(fabs(x-y)<eps)return 0;
	if(x>y)return 1;
	return -1;
}
double cross(PDD a,PDD b)
{
	return a.fi*b.se-b.fi*a.se;
}
double area(PDD a,PDD b,PDD c)
{
	return cross(b-a,c-a);
}
double get_angle(Line a)
{
	return atan2(a.en.se-a.st.se,a.en.fi-a.st.fi);
}
bool cmp(const Line &a,const Line &b)
{
	double A=get_angle(a),B=get_angle(b);
	if(!dcmp(A,B))return area(a.st,a.en,b.en)<0;
	return A<B;
}
PDD get_line_intersection(PDD p,PDD v,PDD q,PDD w)
{
	PDD u=p-q;
	double t=cross(w,u)/cross(v,w);
	return {p.fi+t*v.fi,p.se+t*v.se};
}
PDD get_line_intersection(Line a,Line b)
{
	return get_line_intersection(a.st,a.en-a.st,b.st,b.en-b.st);
}
bool on_right(Line a,Line b,Line c)
{
	PDD o=get_line_intersection(b,c);
	return sign(area(a.st,a.en,o))<=0;
}
double half_plane_intersection()
{
	double res=0;
	sort(line+1,line+cnt+1,cmp);
	int hh=0,tt=-1;
	for(int i=1;i<=cnt;i++)
	{
		if(i>1&&!dcmp(get_angle(line[i]),get_angle(line[i-1])))continue;
		while(hh+1<=tt&&on_right(line[i],line[q[tt-1]],line[q[tt]]))tt--;
		while(hh+1<=tt&&on_right(line[i],line[q[hh]],line[q[hh+1]]))hh++;
		q[++tt]=i;
	}
	while(hh+1<=tt&&on_right(line[q[hh]],line[q[tt-1]],line[q[tt]]))tt--;
	while(hh+1<=tt&&on_right(line[q[tt]],line[q[hh]],line[hh+1]))hh++;
	q[++tt]=q[hh];
	int k=0;
	for(int i=hh;i<tt;i++)ans[++k]=get_line_intersection(line[q[i]],line[q[i+1]]);
	for(int i=2;i+1<=k;i++)res+=area(ans[1],ans[i],ans[i+1]);
	return res/2;
}
int main()
{
    for(scanf("%d",&n);n;n--)
    {
    	scanf("%d",&m);
    	for(int i=0;i<m;i++)scanf("%lf%lf",&a[i].fi,&a[i].se);
    	for(int i=0;i<m;i++)line[++cnt]={a[i],a[(i+1)%m]};
    }
    printf("%.3lf",half_plane_intersection());
    return 0;
}
posted @ 2022-11-16 14:26  zyy2001  阅读(126)  评论(0编辑  收藏  举报