前不久看到格林公式这个东西,想到了这道题,当时还不太懂要怎么处理,直到最近看到了n+e的博客
设$D$为一个平面单连通区域,$L$是它的取正向的轮廓线(分段光滑),$P,Q$在$D$上具有一阶连续偏导数,则有格林公式
$\iint\limits_D\left(\frac{\partial Q}{\partial x}-\frac{\partial P}{\partial y}\right)\mathrm dx\mathrm dy=\oint_LP\mathrm dx+Q\mathrm dy$
证明的话直接按定义分别算$P$和$Q$的部分就可以了,一点都不艺术(雾
在这个公式中,取$Q=x,P=-y$,我们得到$\iint\limits_D2\mathrm dx\mathrm dy=\oint_L-y\mathrm dx+x\mathrm dy$,即是说$D$的面积为$\frac12\oint_L-y\mathrm dx+x\mathrm dy$
这样有个好,就是能把面积转化为跟轮廓线有关的计算
在这题中,我们只需要把所有圆的能够成最后图形的轮廓线的弧找出来做曲线积分,求和即可
圆$C:(x-x_0)^2+(y-y_0)^2=r^2$上一段弧$L$的参数方程为$\begin{cases}x=x_0+r\cos t\\y=y_0+r\sin t\end{cases}(\theta_1\leq t\leq\theta_2)$
$\begin{align*}\oint_L-y\mathrm dx+x\mathrm dy&=\int_{\theta_1}^{\theta_2}-(y_0+r\sin t)\mathrm d(x_0+r\cos t)+(x_0+r\cos t)\mathrm d(y_0+r\sin t)\\&=\int_{\theta_1}^{\theta_2}r^2+ry_0\sin t+rx_0\cos t\mathrm dt\\&=\left.\left(r^2\theta-ry_0\cos t+rx_0\sin t\right)\right|_{\theta_1}^{\theta_2}\end{align*}$
然后就做完了,时间复杂度$O\left(n^2\log_2n\right)$
#include<stdio.h> #include<math.h> #include<algorithm> using namespace std; typedef double du; const du pi2=M_PI*2; struct point{ du x,y; point(du a=0,du b=0){x=a;y=b;} du len(){return sqrt(x*x+y*y);} }; point operator-(point a,point b){return point(a.x-b.x,a.y-b.y);} bool operator<(point a,point b){return a.x==b.x?a.y<b.y:a.x<b.x;} bool operator==(point a,point b){return a.x==b.x&&a.y==b.y;} struct circle{ point o; du r; du oint(du t1,du t2){return r*(r*(t2-t1)+o.x*(sin(t2)-sin(t1))-o.y*((cos(t2)-cos(t1))));} }c[1010]; bool operator<(circle a,circle b){return a.o==b.o?a.r<b.r:a.o<b.o;} bool operator==(circle a,circle b){return a.o==b.o&&a.r==b.r;} struct angle{ du a; int d; angle(du x=0,int y=0){a=x;d=y;} }p[2010]; bool operator<(angle a,angle b){return a.a<b.a;} int n; du solve(int u){ int i,M,cnt; du len,an,f,l,r,s; point d; cnt=0; M=0; for(i=1;i<=n;i++){ if(i!=u){ d=c[i].o-c[u].o; len=d.len(); if(c[u].r+len<=c[i].r)return 0; if(c[u].r>=c[i].r+len||len>=c[i].r+c[u].r)continue; an=atan2(d.y,d.x); f=acos((c[u].r*c[u].r+len*len-c[i].r*c[i].r)/(2*c[u].r*len)); l=an-f; r=an+f; if(l<-M_PI)l+=pi2; if(r>M_PI)r-=pi2; if(l>r)cnt++; p[++M]=angle(l,1); p[++M]=angle(r,-1); } } p[0]=-M_PI; p[++M]=M_PI; sort(p+1,p+M+1); s=0; for(i=1;i<=M;cnt+=p[i++].d){ if(cnt==0)s+=c[u].oint(p[i-1].a,p[i].a); } return s*.5; } int main(){ int i; du s; scanf("%d",&n); for(i=1;i<=n;i++)scanf("%lf%lf%lf",&c[i].o.x,&c[i].o.y,&c[i].r); sort(c+1,c+n+1); n=unique(c+1,c+n+1)-c-1; for(i=1;i<=n;i++)s+=solve(i); printf("%.3lf",s); }