BZOJ 1043: [HAOI2008]下落的圆盘 计几基础

1043: [HAOI2008]下落的圆盘

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1656  Solved: 704
[Submit][Status][Discuss]

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


写这道题让我领悟了一个道理

计几题不要算复杂度 先想出怎么做再说...

n^2的算法很显然 枚举每一个被它压在身下的圆

看它覆盖掉了多大弧度

具体来讲 知道圆心距 两半径 即可用余弦定理求角度

注意判两个圆没有焦点的情况


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;

typedef double db;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=1010;
const db pi=acos(-1),eps=1e-8;

struct point
{
	db x,y;
	
	friend point operator +(const point &a,const point &b)
	{return (point){a.x+b.x,a.y+b.y};}
	
	friend point operator -(const point &a,const point &b)
	{return (point){a.x-b.x,a.y-b.y};}
	
	friend db dot(const point &a,const point &b)
	{return a.x*b.x+a.y*b.y;}
	
	friend db dis(const point &a,const point &b)
	{return sqrt(dot(a-b,a-b));}
};

struct circle
{
	db r;point o;
	
	friend bool in(const circle &a,const circle &b)
	{return a.r+dis(a.o,b.o)<b.r+eps;}
	
	friend bool out(const circle &a,const circle &b)
	{return a.r+b.r<dis(a.o,b.o)+eps || dis(a.o,b.o)+b.r<a.r+eps;}
	
}c[N];

struct arc
{
	db fr,to;
	friend bool operator <(const arc &a,const arc &b)
	{return a.fr<b.fr;}
}a[N][N<<1];

int num[N];

void get_insert(int v,int u)
{
	db d=dis(c[u].o,c[v].o);
	db theta=acos((c[v].r*c[v].r+d*d-c[u].r*c[u].r)/(2*d*c[v].r));
	db alpha=atan2(c[u].o.y-c[v].o.y,c[u].o.x-c[v].o.x);
	if(alpha<0) alpha+=2*pi;
	alpha-=theta;theta=alpha+theta*2;
	if(alpha<0) alpha+=2*pi;
	if(theta>2*pi) theta-=2*pi;
	if(theta<alpha)
	{
		a[v][++num[v]]=(arc){alpha,2*pi};
		a[v][++num[v]]=(arc){0,theta};
	}
	else a[v][++num[v]]=(arc){alpha,theta};
}

bool covered[N];

int main()
{
	int n=read();
	register int i,j;
	for(i=1;i<=n;++i)
		scanf("%lf%lf%lf",&c[i].r,&c[i].o.x,&c[i].o.y);
	for(i=2;i<=n;++i)
		for(j=1;j<i;++j)
			if(!covered[j])
			{
				if(in(c[j],c[i]))
				{covered[j]=1;continue;}
				if(out(c[j],c[i])) continue;
				get_insert(j,i);
			}
	db ans(0);
	for(i=1;i<=n;++i)
		if(!covered[i])
		{
			sort(a[i]+1,a[i]+num[i]+1);
			db remain=2*pi-a[i][1].to+a[i][1].fr,tail=a[i][1].to;
			for(j=2;j<=num[i];++j)
			{
				remain-=a[i][j].to-a[i][j].fr,
				remain+=max(min(tail,a[i][j].to)-a[i][j].fr,0.0),
				tail=max(tail,a[i][j].to);
			}
			ans+=remain*c[i].r;
		}
	printf("%.3lf\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值