「BZOJ 2178」圆的面积并-格林公式

给出 $N$ 个圆,求其面积并。

题解

显然这个题可以用自适应辛普森积分做(然而又慢又难写),格林公式 大法好。

格林公式

设闭区域 $D$ 由分段光滑的简单曲线 $L$ 围成,函数 $P(x, y)$ 及 $Q(x, y)$ 在 $D$ 上有一阶连续偏导数,则有

$$\iint_{D}\frac{\partial Q}{\partial x}-\frac{\partial P}{\partial y}=\oint_{L^+} P \text{ d}x+Q \text{ d}y$$

计算区域面积

使用格林公式,可以用线积分计算区域的面积。因为区域D的面积等于

$$A = \iint_{D}\text{d} A$$

所以只要我们选取适当的 $L$ 与 $M$,使得 $\frac {\partial M} {\partial x} - \frac {\partial L} {\partial y} = 1$,就可以通过

$$A = \oint_{C}(L \text{ d}x + M \text{ d}y)$$

一种可能的取值是

$$A = \oint_{C} x\text{ d}y = - \oint_{C}y \text{ d}x = \frac 1 2 \oint_{C}(-y \text{ d}x + x \text{ d}y)$$

有了这些我们再来考虑这个题。

先写出圆 $C(x_0, y_0, r)$ 的参数方程:

$$\begin{cases}x=x_0+r\cdot \cos\theta \\ y=y_0+r\cdot \sin\theta\end{cases}$$

我们设其在最终图形中所占的边界为 $\theta _1 \sim \theta _2$,那么这个区域面积为

$$\begin{aligned}S &= A \\ &= \frac 1 2 \oint_{C}(-y \text{ d}x + x \text{ d}y) \\ &= \frac{1}{2}\int_{\theta_1}^{\theta_2} (x_0+r\cdot \cos\theta) \text{ d}(y_0+r\cdot \sin\theta)-(y_0+r\cdot \sin\theta) \text{ d}(x_0+r\cdot \cos\theta) \\ &= \frac{r}{2}\int_{\theta_1}^{\theta_2} [(x_0+r\cdot \cos\theta)\cdot \cos\theta +(y_0+r\cdot \sin\theta)\cdot \sin\theta ]\text{ d}\theta \\ &= \frac{r}{2}\int_{\theta_1}^{\theta_2} [x_0\cdot \cos\theta + y_0\cdot \sin\theta+r] \text{ d}\theta \\ &= \frac{1}{2}\cdot(f(r,x_0,y_0,\theta_2)-f(r,x_0,y_0,\theta_1))\end{aligned}$$

其中

$$f(r,x,y,\theta)=r^2\cdot \theta+r\cdot x\cdot \sin\theta-r\cdot y\cdot \cos\theta$$

此时我们对于每个圆算出其对应边界就可以了。

先考虑两个圆的情况,设 $B$ 为当前处理的圆,$A$ 为要并上的圆

BZOJ 2178

我们可以轻松算出 $dis(A, B)$,可以用 atan2 计算 $\beta$,然后我们可以通过余弦定理计算角 $\alpha$,$\theta _2 = \beta - \alpha$,$\theta _1 = \beta + \alpha$。

对于多个圆,我们枚举每个圆,与点 $B$ 进行相同的操作,然后给 $\theta _2$ 打上 $+1$,$\theta _1$ 打上 $-1$ 的标记,然后升序排列,当标记为 $0$ 时计算即可(细节可参考代码)。

时间复杂度 $O(n ^ 2 \log n)$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/**
* Copyright (c) 2017, xehoth
* All rights reserved.
* 「BZOJ 2178」圆的面积并 11-09-2017
* 格林公式
* @author xehoth
*/
#include <bits/stdc++.h>

namespace IO {

inline char read() {
static const int IN_LEN = 1000000;
static char buf[IN_LEN], *s, *t;
s == t ? t = (s = buf) + fread(buf, 1, IN_LEN, stdin) : 0;
return s == t ? -1 : *s++;
}

template <typename T>
inline void read(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return;
c == '-' ? iosig = true : 0;
}
for (x = 0; isdigit(c); c = read()) x = x * 10 + (c ^ '0');
iosig ? x = -x : 0;
}

inline void read(char &c) {
while (c = read(), isspace(c) && c != -1)
;
}

inline int read(char *buf) {
register int s = 0;
register char c;
while (c = read(), isspace(c) && c != -1)
;
if (c == -1) {
*buf = 0;
return -1;
}
do
buf[s++] = c;
while (c = read(), !isspace(c) && c != -1);
buf[s] = 0;
return s;
}
}

namespace {

const int MAXN = 1000;
const double PI = acos(-1);
const double PI2 = PI * 2;

struct Point {
double x, y;

Point(double x = 0, double y = 0) : x(x), y(y) {}

inline Point operator-(const Point &p) const {
return Point(x - p.x, y - p.y);
}

inline double len() { return sqrt(x * x + y * y); }
};

struct Circle {
Point o;
double r;

inline bool operator<(const Circle &a) const {
return o.x != a.o.x ? o.x < a.o.x
: (o.y != a.o.y ? o.y < a.o.y : r < a.r);
}

inline bool operator==(const Circle &a) const {
return o.x == a.o.x && o.y == a.o.y && r == a.r;
}

inline double calc(double t1, double t2) {
return r * (r * (t2 - t1) + o.x * (sin(t2) - sin(t1)) -
o.y * (cos(t2) - cos(t1)));
}

inline void read() {
static int tmp;
IO::read(tmp), o.x = tmp;
IO::read(tmp), o.y = tmp;
IO::read(tmp), r = tmp;
}
};

struct Data {
double x;
int c;
Data(double x = 0, int c = 0) : x(x), c(c) {}
inline bool operator<(const Data &a) const { return x < a.x; }
};

struct Task {
int n;
double ans;
Circle a[MAXN + 2];
Data pos[MAXN * 2 + 4];

inline double solve(int c) {
register int tot = 0, cnt = 0;
for (register int i = 1; i <= n; i++) {
if (i != c) {
Point d = a[i].o - a[c].o;
register double dis = d.len();
if (a[c].r <= a[i].r - dis) return 0;
if (a[i].r <= a[c].r - dis || a[i].r + a[c].r <= dis) continue;
register double beta = atan2(d.y, d.x);
register double alpha =
acos((dis * dis + a[c].r * a[c].r - a[i].r * a[i].r) /
(2 * dis * a[c].r));
register double theta2 = beta - alpha, theta1 = beta + alpha;
if (theta2 < -PI) theta2 += PI2;
if (theta1 >= PI) theta1 -= PI2;
if (theta2 > theta1) cnt++;
pos[++tot] = Data(theta2, 1);
pos[++tot] = Data(theta1, -1);
}
}
pos[0].x = -PI, pos[++tot].x = PI;
std::sort(pos + 1, pos + 1 + tot);
register double ans = 0;
for (register int i = 1; i <= tot; cnt += pos[i++].c)
if (cnt == 0) ans += a[c].calc(pos[i - 1].x, pos[i].x);
return ans;
}

inline void solve() {
IO::read(n);
for (register int i = 1; i <= n; i++) a[i].read();
std::sort(a + 1, a + 1 + n);
n = std::unique(a + 1, a + 1 + n) - a - 1;
for (register int i = 1; i <= n; i++) ans += solve(i);
printf("%.3f\n", ans / 2);
}
} task;
}

int main() {
task.solve();
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×