1. 程式人生 > >【BZOJ1043】[HAOI2008]下落的圓盤 幾何

【BZOJ1043】[HAOI2008]下落的圓盤 幾何

input light www atan flag r+ -1 {} div

【BZOJ1043】[HAOI2008]下落的圓盤

Description

  有n個圓盤從天而降,後面落下的可以蓋住前面的。求最後形成的封閉區域的周長。看下面這副圖, 所有的紅
色線條的總長度即為所求. 技術分享

Input

  第一行為1個整數n,N<=1000
接下來n行每行3個實數,ri,xi,yi,表示下落時第i個圓盤的半徑和圓心坐標.

Output

  最後的周長,保留三位小數

Sample Input

2
1 0 0
1 1 0

Sample Output

10.472

題解:對於每個圓,我們枚舉它後面的所有圓,先判斷後面的圓是否完全覆蓋了當前圓,再考慮相交的情況。我們求出後面的圓覆蓋了當前圓的哪部分,然後我們將圓的周長拉直,那麽每個被覆蓋的部分都可以看成一個線段,求一下這些線段的並即可。

求圓交方法:直接用余弦定理求出覆蓋角度的大小,然後用極角求出角的位置即可。如果角的大小>=2pi或<0,則需要特殊處理。

求線段並方法:我的方法可能有點naive,方法是將線段左端看成+1,右端看成-1,那麽排個序求前綴和,前綴和>0的部分就是被覆蓋的部分。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#define pi acos(-1.0)
using namespace std;
struct circle
{
	double x,y,r;
	circle(){}
	circle(double a,double b)	{x=a,y=b;}
	circle operator + (circle a)	{return circle(x+a.x,y+a.y);}
	circle operator - (circle a)	{return circle(x-a.x,y-a.y);}
	circle operator * (double a)	{return circle(x*a,y*a);}
	circle operator / (double a)	{return circle(x/a,y/a);}
}c[1010];
struct node
{
	double x;
	int v;
}p[2010];
double ans;
double dist(circle a,circle b)	{return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
int n,tot,sum,flag;
bool cmp(node a,node b)
{
	return a.x<b.x;
}
int main()
{
	scanf("%d",&n);
	int i,j;
	for(i=1;i<=n;i++)	scanf("%lf%lf%lf",&c[i].r,&c[i].x,&c[i].y);
	for(i=1;i<=n;i++)
	{
		tot=sum=flag=0;
		for(j=i+1;j<=n;j++)
		{
			double dis=dist(c[i],c[j]);
			if(dis<=c[j].r-c[i].r)
			{
				flag=1;
				break;
			}
			if(dis>fabs(c[i].r-c[j].r)&&dis<=c[i].r+c[j].r)
			{
				double a=acos((c[i].r*c[i].r+dis*dis-c[j].r*c[j].r)/(2*c[i].r*dis));
				double b=atan2(c[j].y-c[i].y,c[j].x-c[i].x);
				p[++tot].x=b-a,p[tot].v=1,p[++tot].x=b+a,p[tot].v=-1;
				if(p[tot-1].x<0)	p[tot-1].x+=2*pi;
				if(p[tot].x<0)	p[tot].x+=2*pi;
				if(p[tot-1].x>=2*pi)	p[tot-1].x-=2*pi;
				if(p[tot].x>=2*pi)	p[tot].x-=2*pi;
				if(p[tot-1].x>p[tot].x)	sum++;
			}
		}
		if(flag)	continue;
		ans+=c[i].r*2*pi;
		if(!tot)	continue;
		sort(p+1,p+tot+1,cmp);
		for(j=1;j<=tot;j++)
		{
			if(sum)	ans-=c[i].r*(p[j].x-p[j-1].x);
			sum+=p[j].v;
		}
		if(sum)	ans-=c[i].r*(2*pi-p[tot].x);
	}
	printf("%.3lf",ans);
	return 0;
}

【BZOJ1043】[HAOI2008]下落的圓盤 幾何