Live2D

Solution -「SPOJ-VCIRCLES」Area of Circles

Description

  Link.

  求平面上 n 个圆的并的面积。

  n50,可能被圆覆盖的横纵坐标区域在 [104,104]

Solution

  做了那么多计算几何之后写了这道不那么计算几何的计算几何题的题解。

  若想直接处理面积,就需要处理圆的各种相交关系,但是仅计算在某个 x0 处,被圆的并覆盖的线段长度是很好处理地:每次 O(nlogn) 排序后扫一遍即可。我们记 f(x) 表示 x0=x 时,被圆的并覆盖的线段长度。所以问题转为求 +f(x)dx

  运用自适应 Simpson 积分,有 Simpson 公式:

abf(x)dxba6(f(a)+4f(a+b2)+f(b))

  其本质是用二次函数 g 近似地替换一个复杂的函数 f,即设:

g(x)=Ax2+Bx+Cf(x)abf(x)dxbag(x)dx

  利用 g(x) 化简积分,注意 g 仅是一种形式,我们的目的使用 f 的一些点值近似表示原积分。推导:

abf(x)dxab(Ax2+Bx+C)dx=A3(b3a3)+B2(b2a2)+C(ba)=2A(b3a3)+3B(b2a2)+6C(ba)6=2A(ba)(a2+ab+b2)+3B(ba)(b+a)+6C(ba)6=ba6(2Aa2+2Aab+2Ab2+3Bb+3Ba+6C)=ba6[(Aa2+Ba+C)+(Ab2+Bb+C)+4(A(a+b2)2+Ba+b2+C)]=ba6(g(a)+4g(a+b2)+g(b))ba6(f(a)+4f(a+b2)+f(b))

  记 h(a,b)=ba6(f(a)+4f(a+b2)+f(b)),自适应 Simpson 积分的表达式如下:

abf(x)dx{h(a,b)h(a,b)h(a,a+b2)+h(a+b2,b)aa+b2f(x)dx+a+b2bf(x)dxotherwise

  其中 根据题目精度要求等自行判断。若精度不合适,则递归求解提升精确度。

  注意自适应 Simpson 积分只能处理平滑函数。例如依照上述算法,02π|sinx|dx=0(大雾)。

  回到本题,结合求 O(nlogn)f(x0) 的方法近似求出 +f(x)dx 即可。复杂度与圆的位置和要求精度有关(aka. 我不知道 qwq)。

Code

  讲道理若某两块并在 x 轴上的投影相离应该分开求积分,但那样写我 WA 掉了 qwq。

/* Clearink */

#include <cmath>
#include <cstdio>
#include <vector>
#include <algorithm>

typedef std::pair<double, double> PDD;

const int MAXN = 50;
const double EPS = 1e-9;
int n;
bool ban[MAXN + 5];

inline double sqr ( const double a ) { return a * a; }
inline double dabs ( const double a ) { return a < 0 ? -a : a; }
inline double dmin ( const double a, const double b ) { return a < b ? a : b; }
inline double dmax ( const double a, const double b ) { return a < b ? b : a; }

struct Circle { double x, y, r; } O[MAXN + 5];

inline double func ( const double x ) {
	static std::vector<PDD> sec; sec.clear ();
	for ( int i = 1; i <= n; ++i ) {
		const Circle& C ( O[i] );
		if ( dabs ( x - C.x ) < C.r ) {
			double yd = sqrt ( sqr ( C.r ) - sqr ( dabs ( C.x - x ) ) );
			sec.push_back ( { C.y - yd, C.y + yd } );
		}
	}
	std::sort ( sec.begin (), sec.end () );
	double las = -1e9, ret = 0;
	for ( PDD s: sec ) {
		las = dmax ( las, s.first );
		ret += dmax ( 0, s.second - las );
		las = dmax ( las, s.second );
	}
	return ret;
}

inline double scalc ( const double lx, const double rx,
	const double fl, const double fm, const double fr ) {
	return ( fl + 4 * fm + fr ) / 6 * ( rx - lx );
}

inline double simpson ( const double lx, const double rx,
	const double fl, const double fm, const double fr ) {
	double mx = 0.5 * ( lx + rx );
	double fll = func ( 0.5 * ( lx + mx ) ), frr = func ( 0.5 * ( mx + rx ) );
	double s = scalc ( lx, rx, fl, fm, fr );
	double sl = scalc ( lx, mx, fl, fll, fm ), sr = scalc ( mx, rx, fm, frr, fr );
	if ( dabs ( s - sl - sr ) < EPS ) return sl + sr;
	return simpson ( lx, mx, fl, fll, fm ) + simpson ( mx, rx, fm, frr, fr );
}

int main () {
	scanf ( "%d", &n );
	double mn = 1e9, mx = -1e9;
	for ( int i = 1; i <= n; ++i ) {
		scanf ( "%lf %lf %lf", &O[i].x, &O[i].y, &O[i].r );
		mn = dmin ( mn, O[i].x - O[i].r );
		mx = dmax ( mx, O[i].x + O[i].r );
	}
	printf ( "%.5f\n", simpson ( mn, mx,
		func ( mn ), func ( 0.5 * ( mn + mx ) ), func ( mx ) ) );
	return 0;
}
posted @   Rainybunny  阅读(82)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示