zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    二分查找练习
    Linux中Buffer和Cache的区别
    c++设计模式之单例模式
    c++设计模式之工厂模式
    C++中内存泄漏的几种情况
    snprintf
    命名空间和模块化编程1
    避免内存泄漏
    静态对象强制类型转换
    副本构造器
  • 原文地址:https://www.cnblogs.com/xh092113/p/12369164.html
Copyright © 2011-2022 走看看