BZOJ 1043: [HAOI2008]下落的圆盘
1043: [HAOI2008]下落的圆盘
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1533 Solved: 644
[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<=1000,n^2可做,按顺序枚举每个圆,再依次枚举之后的所有圆,用数组维护圆上被覆盖的弧度范围,最后类似线段覆盖求出覆盖总和,就可以算出最后可见的长度。
细节:
1.如果当前圆被后面某个圆完全覆盖,那么不考虑和其他圆相交。
2.覆盖的弧度范围要处理一下,如果弧度<0,那么弧度+2π,如果加了之后l>r,那么将l-r拆成0-r和l-2π两段。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=1005;
const double pi=acos(-1.0);
int n,top;
double ans;
struct stack{
double l,r;
}st[N];
struct cir{
double r,x,y;
}c[N];
double sqr(double a){
return a*a;
}
double dis(cir a,cir b){
return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
bool cover(cir a,cir b){
if(a.r>=b.r+dis(a,b))return true;
return false;
}
bool cross(cir a,cir b){
if(a.r+b.r>dis(a,b))return true;
return false;
}
void work(cir a,cir b){
double dist=dis(a,b);
double ang=acos((sqr(a.r)+sqr(dist)-sqr(b.r))/(2*a.r*dist));
double std=atan2((b.y-a.y),(b.x-a.x));
st[++top]=(stack){std-ang,std+ang};
}
bool cmp(stack a,stack b){
return a.l==b.r?a.r<b.r:a.l<b.l;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf",&c[i].r,&c[i].x,&c[i].y);
}
int fg;
double l,r;
for(int i=1;i<=n;i++){
fg=0;
for(int j=i+1;j<=n;j++){
if(cover(c[j],c[i])){
fg=1;
break;
}
}
if(fg)continue;
top=0;
for(int j=i+1;j<=n;j++){
if(cross(c[i],c[j])&&!cover(c[i],c[j]))work(c[i],c[j]);
}
for(int j=1;j<=top;j++){
while(st[j].l<0)st[j].l+=2*pi;
while(st[j].r<0)st[j].r+=2*pi;
if(st[j].l>st[j].r){
st[++top]=(stack){0,st[j].r};
st[j].r=2*pi;
}
}
st[++top]=(stack){2*pi,2*pi};
sort(st+1,st+top+1,cmp);
l=r=0;
for(int j=1;j<=top;j++){
if(st[j].l<r){
if(st[j].r>r)r=st[j].r;
continue;
}
ans-=(r-l)*c[i].r;
l=st[j].l;
r=st[j].r;
}
ans+=2*pi*c[i].r;
}
printf("%.3lf\n",ans);
return 0;
}

浙公网安备 33010602011771号