【bzoj2618】[Cqoi2006]凸多边形 半平面交

题目描述

逆时针给出n个凸多边形的顶点坐标,求它们交的面积。例如n=2时,两个凸多边形如下图:

 

则相交部分的面积为5.233。

输入

第一行有一个整数n,表示凸多边形的个数,以下依次描述各个多边形。第i个多边形的第一行包含一个整数mi,表示多边形的边数,以下mi行每行两个整数,逆时针给出各个顶点的坐标。

输出

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

样例输入

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


题解

半平面交

题意即求一堆半平面的公共部分,即半平面交。

暴力半平面交可以过,但还是学了一下双端队列求半平面交的方法:

不妨设直线的右侧为半平面,那么把所有半平面按照直线的极角从小到大排序,极角相同的保留限制条件最严格的,即最右侧的。

排序去重以后扫一遍所有直线,判断分别队尾交点和队头交点是否在当前直线左端,在的话就踢出双端队列。然后再把当前半平面压入双端队列队尾。

最后,队尾的交点与队首可能不满足条件,因此还要弹掉队尾不合法的部分。

求面积的话直接上叉积就可以了。

废话不多说,直接上代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define eps 1e-9
#define N 510
using namespace std;
struct point
{
	double x , y;
	point() {}
	point(double a , double b) {x = a , y = b;}
	point operator+(const point &a)const {return point(x + a.x , y + a.y);}
	point operator-(const point &a)const {return point(x - a.x , y - a.y);}
	point operator*(const double &a)const {return point(a * x , a * y);}
}p[N];
struct line
{
	point p , v;
	double ang;
}a[N] , q[N] , c[N];
inline double cross(point a , point b) {return a.x * b.y - a.y * b.x;}
inline bool left(line a , point b) {return cross(a.v , b - a.p) > eps;}
inline point inter(line a , line b)
{
	point u = a.p - b.p;
	double tmp = cross(b.v , u) / cross(a.v , b.v);
	return a.p + a.v * tmp;
}
bool cmp(const line &a , const line &b)
{
	return fabs(a.ang - b.ang) < eps ? left(a , b.p) : a.ang < b.ang;
}
int main()
{
	int n , i , j , m , cnt = 0 , tot = 1 , l = 1 , r = 1;
	double ans = 0;
	scanf("%d" , &n);
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%d" , &m);
		for(j = 1 ; j <= m ; j ++ ) scanf("%lf%lf" , &p[j].x , &p[j].y);
		for(j = 1 ; j <= m ; j ++ ) a[++cnt].p = p[j] , a[cnt].v = p[j] - p[j % m + 1] , a[cnt].ang = atan2(a[cnt].v.y , a[cnt].v.x);
	}
	sort(a + 1 , a + cnt + 1 , cmp);
	for(i = 2 ; i <= cnt ; i ++ )
		if(fabs(a[i].ang - a[i - 1].ang) > eps)
			a[++tot] = a[i];
	q[1] = a[1];
	for(i = 2 ; i <= tot ; i ++ )
	{
		while(l < r && left(a[i] , p[r - 1])) r -- ;
		while(l < r && left(a[i] , p[l])) l ++ ;
		q[++r] = a[i];
		if(l < r) p[r - 1] = inter(q[r - 1] , q[r]);
	}
	while(l < r && left(q[l] , p[r - 1])) r -- ;
	p[r] = inter(q[l] , q[r]) , p[r + 1] = p[l];
	for(i = l ; i <= r ; i ++ ) ans += cross(p[i] , p[i + 1]);
	printf("%.3lf\n" , ans / 2);
	return 0;
}

 

posted @ 2017-09-08 16:27  GXZlegend  阅读(399)  评论(0编辑  收藏  举报