BZOJ2178 圆的面积并 计算几何 辛普森积分

原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ2178.html

题目传送门 - BZOJ2178

题意

  给出 $n(n\leq 1000)$ 个圆,求面积并。

  所有圆的圆心坐标和半径都是绝对值不大于 1000 的整数。 

题解

  自适应辛普森积分模板题。注意先删掉被其他圆包含的圆。

  但是 bzoj 大概是加过数据了,网上大部分直接自适应辛普森的代码都 TLE 了。

  有一种卡常方法效果很好:

  把 x 坐标按照整点划分成 $O(1000)$ 个区间,对于每一个区间,删除不涉及这个区间的所有圆。然后就过了。

 

代码

#include <bits/stdc++.h>
#define y1 __zzd12323
using namespace std;
const int N=1005;
const double Eps=1e-9,INF=1e9;
double sqr(double x){
	return x*x;
}
int n;
struct Circle{
	double x,y,r;
}c[N],_c[N];
bool cmpr(Circle a,Circle b){
	return a.r>b.r;
}
struct Seg{
	double L,R;
}s[N];
bool cmp(Seg a,Seg b){
	return a.L<b.L;
}
double F(double x){
	int m=0;
	for (int i=1;i<=n;i++){
		if (fabs(x-c[i].x)>c[i].r-Eps)
			continue;
		double M=c[i].y,a=sqrt(sqr(c[i].r)-sqr(x-c[i].x));
		s[++m].L=M-a,s[m].R=M+a;
	}
	sort(s+1,s+m+1,cmp);
	double res=0.0,mx=-INF;
	for (int i=1;i<=m;i++)
		if (s[i].L>mx)
			res+=s[i].R-s[i].L,mx=s[i].R;
		else if (s[i].R>mx)
			res+=s[i].R-mx,mx=s[i].R;
	return res;
}
double Simpson(double a,double b,double Fa,double Fb,double Fab){
	return (b-a)/6*(Fa+Fab*4+Fb);
}
double Asr(double a,double b,double v,double Fa,double Fb,double Fab){
	double ab=(a+b)/2;
	double LFab=F((a+ab)/2),L=Simpson(a,ab,Fa,Fab,LFab);
	double RFab=F((ab+b)/2),R=Simpson(ab,b,Fab,Fb,RFab);
	if (fabs(v-L-R)<Eps)
		return v;
	return Asr(a,ab,L,Fa,Fab,LFab)
	      +Asr(ab,b,R,Fab,Fb,RFab);
}
int killed[N];
double Dis(double x1,double y1,double x2,double y2){
	return sqrt(sqr(x1-x2)+sqr(y1-y2));
}
int main(){
	scanf("%d",&n);
	double a=INF,b=-INF;
	for (int i=1;i<=n;i++){
		scanf("%lf%lf%lf",&c[i].x,&c[i].y,&c[i].r);
		a=min(a,c[i].x-c[i].r);
		b=max(b,c[i].x+c[i].r);
	}
	sort(c+1,c+n+1,cmpr);
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			if (Dis(c[i].x,c[i].y,c[j].x,c[j].y)+c[j].r<c[i].r+Eps)
				killed[j]=1;
	int _n=0;
	for (int i=1;i<=n;i++)
		if (!killed[i])
			c[++_n]=c[i];
	n=_n;
	double ans=0;
	for (double x=a;x<b;x++){
		for (int i=1;i<=n;i++)
			_c[i]=c[i];
		int _n=n;
		n=0;
		for (int i=1;i<=_n;i++)
			if (c[i].x-c[i].r<i+1&&c[i].x+c[i].r>i)
				c[++n]=_c[i];
		n=_n;		
		for (int i=1;i<=n;i++)
			c[i]=_c[i];
		double Fa=F(x),Fb=F(x+1),Fab=F((x+x+1)/2);
		ans+=Asr(x,x+1,Simpson(x,x+1,Fa,Fb,Fab),Fa,Fb,Fab);
	}
	printf("%.3lf",ans);
	return 0;
}

  

posted @ 2018-09-06 10:38  zzd233  阅读(311)  评论(0编辑  收藏  举报