BZOJ2829信用卡凸包——凸包

题目描述

输入

输出

样例输入

2
6.0 2.0 0.0
0.0 0.0 0.0
2.0 -2.0 1.5707963268

样例输出

21.66

提示


本样例中的2张信用卡的轮廓在上图中用实线标出,如果视1.5707963268为
Pi/2(pi为圆周率),则其凸包的周长为16+4*sqrt(2)

 

我们先将每个信用卡看成是由四个圆心组成的矩形,那么凸包就是所有圆心形成的凸包。现在每个圆心往外扩展为一个圆,那么就是用凸包来包住所有的圆,而新的凸包的直边部分就是原凸包对应边往外平移得到。对于曲面部分,因为一个凸多边形外角和为360度,所以曲面部分就是一个圆的周长。直接求所有圆心形成凸包的周长再加上一个圆的周长即可。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const double pi=acos(-1.0);
struct lty
{
	double x,y;
	lty(double X=0,double Y=0){x=X,y=Y;}
}a[40010];
lty operator -(lty a,lty b){return lty(a.x-b.x,a.y-b.y);}
double operator *(lty a,lty b){return a.x*b.y-b.x*a.y;}
int st[40010];
int vis[40010];
int top;
int n;
int cnt;
double x,y,r;
double X,Y,R;
double ans;
lty res;
bool cmp(lty a,lty b)
{
	return a.x==b.x?a.y<b.y:a.x<b.x;
}
int main()
{
	scanf("%d",&n);
	scanf("%lf%lf%lf",&X,&Y,&R);
	X=X/2-R,Y=Y/2-R;
	for(int i=1;i<=n;i++)
	{
		scanf("%lf%lf%lf",&x,&y,&r);
		a[++cnt]=lty(x+Y*cos(r)+X*sin(r),y+Y*sin(r)-X*cos(r));
		a[++cnt]=lty(x-Y*cos(r)+X*sin(r),y-Y*sin(r)-X*cos(r));
		a[++cnt]=lty(x+Y*cos(r)-X*sin(r),y+Y*sin(r)+X*cos(r));
		a[++cnt]=lty(x-Y*cos(r)-X*sin(r),y-Y*sin(r)+X*cos(r));
	}
	sort(a+1,a+1+cnt,cmp);
	st[++top]=1;
	for(int i=2;i<=cnt;i++)
	{
		while(top>1&&(a[st[top]]-a[st[top-1]])*(a[i]-a[st[top]])<=0)
		{
			vis[st[top--]]=0;
		}
		vis[i]=1;
		st[++top]=i;
	}
	int lim=top;
	for(int i=cnt-1;i>=1;i--)
	{
		if(!vis[i])
		{
			while(top>lim&&(a[st[top]]-a[st[top-1]])*(a[i]-a[st[top]])<=0)
			{
				vis[st[top--]]=0;
			}
			vis[i]=1;
			st[++top]=i;
		}
	}
	for(int i=1;i<top;i++)
	{
		res=a[st[i+1]]-a[st[i]];
		ans+=sqrt(res.x*res.x+res.y*res.y);
	}
	printf("%.2f",ans+2*pi*R);
}
posted @ 2019-03-27 18:52  The_Virtuoso  阅读(268)  评论(0编辑  收藏  举报