计算几何基础
对于一个圆,它对答案的贡献就是周长-被覆盖的长度,那么重点就是求一个圆被另一个圆覆盖的长度。
我们可以把这个圆沿x正半轴顺时针展开,那么这个圆就变成了一条线段。其它圆的覆盖就变成了线段覆盖了。
两个圆相交的情况如下图所示,利用余弦定理可以求出交点与两圆心连线的夹角,再利用atan2函数求出极角,就可以对应到区间上了。
注意判断 <0 < 0 <script type="math/tex" id="MathJax-Element-17"><0</script>或 >2π > 2 π 的情况。
代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1005
#define sqr(x) ((x)*(x))
using namespace std;
typedef double DB;
const DB pi=2*acos(-1.0);
struct seg{ DB l,r; }c[N<<1];
int n,sum;
DB ans,r[N],x[N],y[N];
inline bool cmp(seg a,seg b){ return a.l<b.l; }
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%lf%lf%lf",&r[i],&x[i],&y[i]);
for (int i=1;i<=n;i++){
ans+=pi*r[i],sum=0;
for (int j=i+1;j<=n;j++){
DB d=sqr(x[i]-x[j])+sqr(y[i]-y[j]);
if (sqr(r[i]+r[j])<=d)
c[++sum]=(seg){0,0};
else
if (sqr(r[i]-r[j])>=d)
c[++sum]=(seg){0,r[i]>r[j]?0:pi};
else{
DB a=acos((r[i]*r[i]-r[j]*r[j]+d)/(2*r[i]*sqrt(d)));
DB b=atan2(y[j]-y[i],x[j]-x[i]);
if (b<0) b+=pi; c[++sum]=(seg){b-a,b+a};
if (c[sum].l<0)
c[++sum]=(seg){c[sum-1].l+pi,pi},c[sum-1].l=0;
else if (c[sum].r>pi)
c[++sum]=(seg){0,c[sum-1].r-pi},c[sum-1].r=pi;
}
}
sort(c+1,c+sum+1,cmp); DB lst=0;
for (int j=1;j<=sum;j++){
if (c[j].r<=lst) continue;
if (c[j].l>lst) ans-=(c[j].r-c[j].l)*r[i];
else ans-=(c[j].r-lst)*r[i];
lst=c[j].r;
}
}
return printf("%.3f\n",ans),0;
}