(这几天好好学习计算几何(flag立起?))对于每一个i,求出这个圆盘会被后面的每一个圆盘覆盖哪一段。对于一个圆,可以用-pi到pi的弧度表示区间,然后就可以用线段覆盖的方法求出区间被覆盖了多少了。
因此关键是对于两个圆盘i,j,如何求出i被覆盖的区间。首先对于两个圆的圆心x,y,可以求出向量(y-x)的atan2值,相当于与x轴正方向的夹角α,范围-pi到pi。然后根据余弦定理cos∠A=(b^2+c^2-a^2)/2bc,得到x的圆心到两圆的一个焦点的射线关于两圆连心线旋转角的余弦,然后用acos得到旋转角β,那么α±β就是对应的区间。注意边界问题,如果下界<-pi,就变成下界+2pi->pi,上界>pi同理。
注意两圆不相交的情况。
AC代码如下(似乎并没有卡精度?):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define pi acos(-1.0)
#define N 2005
using namespace std;
int n,cnt;
struct point{ double x,y; }c[N];
struct circle{ point o; double r; }a[N];
double dist(point u,point v){
return sqrt((u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y));
}
void add(double u,double v){ c[++cnt].x=u; c[cnt].y=v; }
void solve(circle u,circle v,double tmp){
double t1=atan2(v.o.y-u.o.y,v.o.x-u.o.x),t2=acos((u.r*u.r+tmp*tmp-v.r*v.r)/(u.r*tmp*2));
point q; q.x=t1-t2; q.y=t1+t2;
if (q.x>=-pi && q.y<=pi) add(q.x,q.y); else
if (q.x<-pi){ add(q.x+2*pi,pi); add(-pi,q.y); } else{
add(q.x,pi); add(-pi,q.y-2*pi);
}
}
bool cmp(point x,point y){ return x.x<y.x; }
double calc(){
int i; double t=0,l=-10,r=-10; sort(c+1,c+cnt+1,cmp);
for (i=1; i<=cnt; i++)
if (c[i].x>r){
t+=r-l; l=c[i].x; r=c[i].y;
} else r=max(r,c[i].y);
t+=r-l; return 2*pi-t;
}
int main(){
scanf("%d",&n); int i,j; double ans=0;
for (i=n; i; i--)
scanf("%lf%lf%lf",&a[i].r,&a[i].o.x,&a[i].o.y);
for (i=1; i<=n; i++){
cnt=0;
for (j=1; j<i; j++){
double tmp=dist(a[i].o,a[j].o);
if (a[j].r-a[i].r>tmp) break;
if (a[j].r+a[i].r>tmp && fabs(a[j].r-a[i].r)<tmp) solve(a[i],a[j],tmp);
}
if (j==i) ans+=a[i].r*calc();
}
printf("%.3f\n",ans);
return 0;
}
by lych
2016.3.1