[SPOJ8073] CIRU [自适应Simpson/格林公式][计算几何]

Link
Luogu - https://www.luogu.org/problemnew/show/SP8073
BZOJ - https://www.lydsy.com/JudgeOnline/problem.php?id=2178


我就不讲Simpson了


Green Formula
对平面单联通区域 D \rm D D 及其正向轮廓线 L \rm L L (分段光滑),且 P , Q P,Q P,Q D \rm D D 上有一阶连续偏导
∬ D ( ∂ Q ∂ x − ∂ P ∂ y ) d x d y = ∮ L P d x + Q d y \mathrm{\iint\limits_{_D}}\left(\frac{\partial Q}{\partial x}-\frac{\partial P}{\partial y}\right)dxdy=\mathrm{\oint\limits_{_L}}Pdx+Qdy D(xQyP)dxdy=LPdx+Qdy


Q = x , P = − y Q=x,P=-y Q=x,P=y 得到 ∬ D 2 d x d y = ∮ L ( − y d x + x d y ) \iint\limits_{_{\rm D}}2dxdy=\oint\limits_{_{\rm L}}(\pmb-ydx+xdy) D2dxdy=L(ydx+xdy)
那么 ∬ D d x d y = 1 2 ∮ L ( − y d x + x d y ) \iint\limits_{\rm D}dxdy=\frac{1}{2}\oint\limits_{\rm L}(\pmb-ydx+xdy) Ddxdy=21L(ydx+xdy)


C C C 上弧 L L L 参数方程 { x = x 0 + r cos ⁡ t y = y 0 + r sin ⁡ t , t ∈ [ θ 1 , θ 2 ] \begin{cases}\begin{array}{rcl} x&=&x_0+r\cos t&\\ y&=&y_0+r\sin t& \end{array},\quad t\in[\theta_1,\theta_2]\end{cases} {xy==x0+rcosty0+rsint,t[θ1,θ2]

S = ∮ L ( − y d x + x d y ) = ∫ θ 1 θ 2 − ( y 0 + r sin ⁡ t ) d ( x 0 + r cos ⁡ t ) + ( x 0 + r cos ⁡ t ) d ( y 0 + r sin ⁡ t ) = ⋯ = ∫ θ 1 θ 2 ( r 2 + x 0 r cos ⁡ t + y 0 r sin ⁡ t )    d t \begin{array}{rcl} S=\displaystyle\oint\limits_L(\pmb-ydx+xdy)&=&\displaystyle\int\limits_{\theta_1}^{\theta_2}\pmb-(y_0+r\sin t)d(x_0+r\cos t)+(x_0+r\cos t)d(y_0+r\sin t)\\ &=&\cdots\\ &=&\displaystyle\int\limits_{\theta_1}^{\theta_2}(r^2+x_0r\cos t+y_0r\sin t)\;dt \end{array} S=L(ydx+xdy)===θ1θ2(y0+rsint)d(x0+rcost)+(x0+rcost)d(y0+rsint)θ1θ2(r2+x0rcost+y0rsint)dt
(中间公式被吞过,懒得再打一遍了……反正就那样嘛,不懂微分怎么算的话百度微分运算法则)
然后最后对每个圆算一下产生贡献的弧,这个差分一下就可以辣

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN = 1005;
const double eps = 1e-12;
const double tpi = acos(-1.0);
int n;
double Ans;
struct Circle {
	double x, y, r;
	inline double f(const double& t) {
		return (t * r + sin(t) * x - cos(t) * y) * r;
	}
	inline double oint(const double& theta_l, const double theta_r) {
		return r * (r * (theta_r - theta_l) + x * (sin(theta_r) - sin(theta_l)) - y * (cos(theta_r) - cos(theta_l)));
//		return f(theta_r) - f(theta_l);
	}
	inline bool operator == (const Circle& o) const {
		return (x==o.x)&&(y==o.y)&&(r==o.r);
	}
	inline bool operator < (const Circle& o) const {
		return (x==o.x)?((y==o.y)?(r<o.r):(y<o.y)):(x<o.x);
	}
}C[MAXN];
inline double defabs(const double&x) {
	return (x<0)?(-x):x;
}
int tot;
pair<double, bool> Pt[(MAXN<<1)+5];
inline void Calc(const int& x) {
	tot = 0;
	register int Sm = 0;
	register double DistX, DistY, Dist, Angle, Cosine, Theta, Thetb;
	for (register int i = 1; i <= n; ++i) {
		if (i == x) continue;
		DistX = C[i].x - C[x].x;
		DistY = C[i].y - C[x].y;
		Dist = sqrt(DistX * DistX + DistY * DistY);
		if (C[x].r + Dist <= C[i].r + eps) return;
		if (C[i].r + Dist <= C[x].r + eps || Dist >= C[i].r + C[x].r - eps) continue;
		Angle = atan2(DistY, DistX),
		Cosine = acos((Dist*Dist+C[x].r*C[x].r-C[i].r*C[i].r)/(2.0*Dist*C[x].r));
		Theta = Angle - Cosine;
		Thetb = Angle + Cosine;
		if (Theta < -tpi) Theta += tpi * 2;
		if (Thetb >= tpi) Thetb -= tpi * 2;
		if (Theta > Thetb) ++Sm;
		Pt[++tot] = make_pair(Theta, 0);
		Pt[++tot] = make_pair(Thetb, 1);
	}
	Pt[0] = make_pair(-tpi, 0);
	Pt[++tot] = make_pair(tpi, 0);
	sort(Pt + 1,Pt + tot);
	for (register int i = 1; i <= tot; ++i) {
		if (!Sm) Ans += C[x].oint(Pt[i-1].first, Pt[i].first);
		(Pt[i].second)?(--Sm):(++Sm);
	}
}
int main() {
	scanf("%d", &n);
	for (register int i = 0, j = 1; j <= n; ++j) {
		++i;
		scanf("%lf%lf%lf", &C[i].x , &C[i].y, &C[i].r);
	}
	sort(C + 1, C + 1 + n);
	n = unique(C + 1, C + 1 + n) - C - 1;
	for (register int i = 1; i <= n; ++i) {
		Calc(i);
	}
	printf("%.3f", Ans / 2);
	return 0;
}

没有名字的一般计算几何方法
好!


当然也可以求面积交
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN = 1005;
const double eps = 1e-12;
const double tpi = acos(-1.0);
int n;
double Ans;
struct Circle {
	double x, y, r;
	inline double f(const double& t) {
		return (t * r + sin(t) * x - cos(t) * y) * r;
	}
	inline double oint(const double& theta_l, const double theta_r) {
		return r * (r * (theta_r - theta_l) + x * (sin(theta_r) - sin(theta_l)) - y * (cos(theta_r) - cos(theta_l)));
//		return f(theta_r) - f(theta_l);
	}
	inline bool operator == (const Circle& o) const {
		return (x==o.x)&&(y==o.y)&&(r==o.r);
	}
	inline bool operator < (const Circle& o) const {
		return (x==o.x)?((y==o.y)?(r<o.r):(y<o.y)):(x<o.x);
	}
}C[MAXN];
inline double defabs(const double&x) {
	return (x<0)?(-x):x;
}
int tot;
pair<double, bool> Pt[(MAXN<<1)+5];
inline void Calc(const int& x) {
	tot = 0;
	register int Sm = 0;
	register double DistX, DistY, Dist, Angle, Cosine, Theta, Thetb;
	for (register int i = 1; i <= n; ++i) {
		if (i == x) continue;
		DistX = C[i].x - C[x].x;
		DistY = C[i].y - C[x].y;
		Dist = sqrt(DistX * DistX + DistY * DistY);
		if (C[x].r + Dist <= C[i].r + eps) return;
		if (C[i].r + Dist <= C[x].r + eps || Dist >= C[i].r + C[x].r - eps) continue;
		Angle = atan2(DistY, DistX),
		Cosine = acos((Dist*Dist+C[x].r*C[x].r-C[i].r*C[i].r)/(2.0*Dist*C[x].r));
		Theta = Angle - Cosine;
		Thetb = Angle + Cosine;
		if (Theta < -tpi) Theta += tpi * 2;
		if (Thetb >= tpi) Thetb -= tpi * 2;
		if (Theta > Thetb) ++Sm;
		Pt[++tot] = make_pair(Theta, 0);
		Pt[++tot] = make_pair(Thetb, 1);
	}
	Pt[0] = make_pair(-tpi, 0);
	Pt[++tot] = make_pair(tpi, 0);
	sort(Pt + 1,Pt + tot);
	for (register int i = 1; i <= tot; ++i) {
		if (!Sm) Ans += C[x].oint(Pt[i-1].first, Pt[i].first);
		(Pt[i].second)?(--Sm):(++Sm);
	}
}
int main() {
	double Tem = 0;
	scanf("%d", &n);
	for (register int i = 1; i <= n; ++i) {
		scanf("%lf%lf%lf", &C[i].x , &C[i].y, &C[i].r);
		Tem += C[i].r * C[i].r * tpi;
	}
	sort(C + 1, C + 1 + n);
	n = unique(C + 1, C + 1 + n) - C - 1;
	for (register int i = 1; i <= n; ++i) {
		Calc(i);
	}
	printf("%.3f", Tem - Ans / 2);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷的SPOJ需要注册一个SPOJ账号并进行绑定才能进行交题。您可以按照以下步骤进行注册: 1. 打开洛谷网站(https://www.luogu.com.cn/)并登录您的洛谷账号。 2. 在网站顶部导航栏中找到“题库”选项,将鼠标悬停在上面,然后选择“SPOJ”。 3. 在SPOJ页面上,您会看到一个提示,要求您注册SPOJ账号并进行绑定。点击提示中的链接,将会跳转到SPOJ注册页面。 4. 在SPOJ注册页面上,按照要求填写您的用户名、密码和邮箱等信息,并完成注册。 5. 注册完成后,返回洛谷网站,再次进入SPOJ页面。您会看到一个输入框,要求您输入刚刚注册的SPOJ用户名。输入用户名后,点击“绑定”按钮即可完成绑定。 现在您已经成功注册并绑定了SPOJ账号,可以开始在洛谷的SPOJ题库上刷题了。祝您顺利完成编程练习!\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [(洛谷入门系列,适合洛谷新用户)洛谷功能全解](https://blog.csdn.net/rrc12345/article/details/122500057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [luogu p7492 序列](https://blog.csdn.net/zhu_yin233/article/details/122051384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值