SGU435 UFO Circles 题解
题目简意
给出平面直角坐标系中若干个互不相同的圆,分别求出求被这些圆覆盖奇数次的区域的面积和覆盖偶数次(至少一次)的区域的面积。
题解
前置知识 :格林公式
大学里面微积分里面的内容,这里我们直接讲结论不讲证明(其实是我不是很懂微积分,并不会证,下面讲的内容凭我个人理解,不保证严谨正确),有兴趣的同学可以继续看,其他同学可以直接下翻到格林公式在 OI 里的应用。
格林公式:设闭区域
看不懂没关系,大概解释一下它讲了个啥。首先对于闭区域
在 OI 中,格林公式常用于求解圆形面积并。我们令
左边那坨不就是区域
考虑一个圆心坐标为
直接带到公式里面去:
积分的推导过程省略了(其实还是我不是很懂),
最后得到的这个公式告诉我们,在求圆形面积并的时候只需要关心组成圆形并的轮廓的那些圆弧的参数,对于每一段弧,带入公式求出答案,全部加起来就是圆形面积并。至于那些圆弧怎么求,下面再讲。
回归本题
这个问题有点类似圆形面积并,因为第一问的答案加第二问的答案就是圆形面积并,我们考虑使用格林公式解决这个问题。画几个图可以发现一个重要的性质:被覆盖了
举个例子:
红色/蓝色/绿色标注的弧是被其他圆覆盖
再稍微把这个结论转化一下:至少被覆盖了
对于
举个例子,假设中心圆是上面那张图右下角的圆,那么发现它左边的圆和上面的圆都会覆盖到它的一部分圆弧,形成了覆盖次数分别是
时间复杂度
实际上,圆形面积并求的就是
代码
#include <bits/stdc++.h>
#define re(i, x, y) for (int i = (x); i < (y); ++i)
#define rep(i, x, y) for (int i = (x); i <= (y); ++i)
using namespace std;
using db = long double;
const db pi = acosl(-1.0), eps = 1e-12;
int n, m;
int d[205];
db ky[205], S[105];
pair<db, db> arc[205];
struct circ {
int x, y, r;
} a[105];
db f(circ c, db th) { // 格林公式
return c.r * (c.x * sinl(th) - c.y * cosl(th) + c.r * th);
}
int main() {
#ifdef sword
freopen("test.in", "r", stdin);
#endif
scanf("%d", &n);
rep(i, 1, n) {
scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].r);
}
rep(i, 1, n) {
int cov = 0, t = 0, k = 0;
ky[++k] = -pi, ky[++k] = pi;
rep(j, 1, n) {
if (i == j) {
continue;
}
db d = hypotl(a[i].x - a[j].x, a[i].y - a[j].y);
if (d >= a[i].r + a[j].r) {
continue;
}
if (a[i].r + d <= a[j].r) {
++cov; // 注意一个圆完全被另一个圆包含
continue;
}
if (a[j].r + d <= a[i].r) {
continue;
}
db th = atan2l(a[j].y - a[i].y, a[j].x - a[i].x), de = acosl((a[i].r * a[i].r + d * d - a[j].r * a[j].r) / 2 / a[i].r / d);
db l = th - de, r = th + de;
if (r > pi) {
r -= 2 * pi;
}
if (l - eps < -pi) {
l += 2 * pi;
}
ky[++k] = l, ky[++k] = r;
if (l <= r) {
arc[++t] = make_pair(l, r);
}
else {
arc[++t] = make_pair(l, pi);
arc[++t] = make_pair(-pi, r);
}
}
sort(ky + 1, ky + k + 1);
k = unique(ky + 1, ky + k + 1) - ky - 1;
memset(d, 0, sizeof(d));
rep(j, 1, t) {
int l = lower_bound(ky + 1, ky + k + 1, arc[j].first) - ky, r = lower_bound(ky + 1, ky + k + 1, arc[j].second) - ky;
++d[l], --d[r];
}
re(j, 1, k) {
d[j] += d[j - 1];
S[cov + d[j]] += f(a[i], ky[j + 1]) - f(a[i], ky[j]);
}
}
db a1 = 0, a2 = 0;
re(i, 0, n) {
S[i] -= S[i + 1];
(i & 1 ? a2 : a1) += S[i];
}
printf("%.6Lf %.6Lf\n", a1 / 2, a2 / 2); // 把 1/2 提出来最后算
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】