[BZOJ2178]圆的面积并(格林公式)

题面

http://darkbzoj.tk/problem/2178

题解

前置知识

  • 格林公式:《高等数学》11.3节

因为本题对精度要求不高(并不是,测试点13警告),也可以用自适应Simpson近似(题解);不过那毕竟是近似,格林公式相较于Simpson近似,速度既快,还可以求出精确值。

我们有格林公式

[{iintlimits_{D}}({frac{partial Q}{partial x}}-{frac{partial P}{partial y}})dxdy={ointlimits_{L^+}}Pdx+Qdy ]

其中D是我们要求面积的图形,(L)是此图形边界曲线,(+)保证了其方向:当观察者在(L)上按此方向行走时,D内在他近处的那一部分总在他的左边。

(P(x,y)=-y,Q(x,y)=x),那么等式左边就是两倍面积,所以

[S={frac{1}{2}}{ointlimits_{L^+}}xdy-ydx ]

(其实如果不懂格林公式,等式右边的式子也可以按照多边形求和公式的方式理解)

对于此题,把(L^+)分解成每一个圆上的没有被其他圆覆盖部分,而这个部分一定由一些圆弧组成。所以我们要做的就是对于圆弧(I),求出

[{ointlimits_{I逆时针方向}}xdy-ydx ]

设这条圆弧是在圆((x-x_0)^2+(y-y_0)^2=r^2)上、对应的极角是([{ heta_l},{ heta_r}])。(如果跨过x轴负半轴,那么拆成两段)换元,上式就是

[{int_{ heta_l}^{ heta_r}}((x_0+r cos heta)frac{d(y_0+rsin heta)}{d heta}-(y_0+rsin heta){frac{d(x_0+rcos heta)}{d heta}})d heta ]

[={int_{ heta_l}^{ heta_r}}((x_0+r cos heta)rcos heta+(y_0+rsin heta)rsin heta)d heta ]

[={int_{ heta_l}^{ heta_r}}(x_0rcos heta+y_0rsin heta+r^2)d heta ]

[=r^2( heta_r- heta_l)^2+x_0r(sin( heta_r)-sin( heta_l))-y_0r(cos( heta_r)-cos( heta_l)) ]

这就解决了本题。

时间复杂度(O(n^2log n))

代码

  • 实现有一些细节,比如重合、内含、外离、r=0等等,需要特判。
#include<bits/stdc++.h>

using namespace std;

#define ld long double
#define In inline
#define rg register

const int N = 1000;
const ld eps = 1e-9;
const ld pi = acosl(-1.0);

typedef pair<ld,ld>pll;

In int read(){
	int s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

In int sgn(ld x){
	return x < -eps ? -1 : x > eps;
}

In ld sqr(ld x){
	return x * x;
}

struct vec{
	ld x,y;
	vec(){}
	vec(ld _x,ld _y){x = _x,y = _y;}
	In friend vec operator + (vec a,vec b){
		return vec(a.x + b.x,a.y + b.y);
	}
	In friend vec operator - (vec a,vec b){
		return vec(a.x - b.x,a.y - b.y);
	}
	In friend ld len(vec a){
		return sqrt(sqr(a.x) + sqr(a.y));
	}
};

struct cir{
	vec c;ld r;
	cir(){}
	cir(vec _c,ld _r){c = _c,r = _r;};
	In friend bool HaveIts(cir a,cir b){ //非外离或外切
		ld dis = len(a.c - b.c);
		return sgn(dis - (a.r+b.r)) < 0;
	}
	In friend bool include(cir a,cir b){ //b内含于或内切于a
		if(sgn(a.r - b.r) < 0)return 0;
		ld dis = len(a.c - b.c);
		return sgn(dis - fabs(a.r-b.r)) <= 0;
	}
}l[N+5]; //loop

In bool cmp1(cir a,cir b){
	int k;
	if((k=sgn(a.c.x-b.c.x)) != 0)return k < 0;
	if((k=sgn(a.c.y-b.c.y)) != 0)return k < 0;
	return sgn(a.r - b.r) < 0;
}

In bool cmp2(cir a,cir b){
	return sgn(a.c.x - b.c.x) == 0
		&& sgn(a.c.y - b.c.y) == 0
		&& sgn(a.r - b.r) == 0;
} //equal

pll intv[2*N+5]; //覆盖部分

int n;

In ld calc(ld L,ld R,int id){
	return sqr(l[id].r) * (R - L) + l[id].r * (l[id].c.x*(sinl(R)-sinl(L)) - l[id].c.y*(cosl(R)-cosl(L)));
}

ld f(int id){
	int cnt = 0;
	if(sgn(l[id].r) <= 0)return 0;
	for(rg int i = 1;i <= n;i++){
		if(i == id)continue;
		if(include(l[i],l[id]))return 0; //被内含
		if(!HaveIts(l[i],l[id]) || include(l[id],l[i]))continue; //无交点
		ld a = atan2l(l[i].c.y - l[id].c.y,l[i].c.x - l[id].c.x);
		ld r1 = l[id].r,r2 = l[i].r,dis = len(l[i].c - l[id].c);
		ld t = acosl((sqr(r1)+sqr(dis)-sqr(r2)) / (2*r1*dis));
		ld l = a - t,r = a + t;
		while(sgn(l + pi) < 0)l += 2 * pi;
		while(sgn(r - pi) >= 0)r -= 2 * pi;
		if(sgn(l-r) <= 0)intv[++cnt] = make_pair(l,r);
		else{
			intv[++cnt] = make_pair(l,pi);
			intv[++cnt] = make_pair(-pi,r);
		}
	}
	if(cnt)sort(intv + 1,intv + cnt + 1);
	ld ans = 0;
	ld L = -pi,R = -pi;
	for(rg int i = 1;i <= cnt;i++){
		if(sgn(intv[i].first-R) >= 0)ans += calc(R,intv[i].first,id),L = R = intv[i].first;
		if(sgn(intv[i].second-R) >= 0)R = intv[i].second;
	}
	ans += calc(R,pi,id);
	return ans / 2;
}

int main(){
	n = read();
	for(rg int i = 1;i <= n;i++){
		int x = read(),y = read(),r = read();
		l[i] = cir(vec(x,y),r);
	}
	sort(l + 1,l + n + 1,cmp1);
	n = unique(l + 1,l + n + 1,cmp2) - l - 1; //去重
	ld ans = 0;
	for(rg int i = 1;i <= n;i++)ans += f(i);
	printf("%.3lf
",(double)ans);
	return 0;
}
原文地址:https://www.cnblogs.com/xh092113/p/12369164.html