Abstract
ural 1913 Titan Ruins: Old Generators Are Fine Too
implementation 几何 圆交
Source
http://acm.timus.ru/problem.aspx?space=1&num=1913
Solution
主要思路就是判断距离到某两个点是r,另一个点是2r的点。
比赛时候用三分乱搞,大概是被卡了精度还是什么的,没有通过。
赛后用圆交重写了一遍,一开始还是没通过。
然后发现自己的圆交模板是针对面积问题的,单点的情况没有考虑清楚,乱改的时候改错了。大致就是 处理圆覆盖的情况时,在预处理统计时算被覆盖了一次,在算两圆交点的时候又被统计了一次 。还是没弄熟悉圆交的原理所致。
Code
#include <iostream>
#include
<cassert>
#include
<cmath>
#include
<cstdio>
#include
<cstring>
#include
<algorithm>
using
namespace
std;
const
double
EPS = 1e-
8
;
const
double
PI = acos(-
1.0
);
inline
int
sgn(
double
x) {
if
(fabs(x)<EPS)
return
0
;
return
x>
0
?
1
:-
1
;
}
struct
sp {
double
x, y;
double
pa;
int
cnt;
sp() {}
sp(
double
a,
double
b): x(a), y(b) {}
sp(
double
a,
double
b,
double
c,
int
d): x(a), y(b), pa(c), cnt(d) {}
bool
operator
<(
const
sp &rhs)
const
{
if
(sgn(pa-rhs.pa)==
0
)
return
cnt>
rhs.cnt;
return
pa<
rhs.pa;
}
void
read() {scanf(
"
%lf%lf
"
, &x, &
y);}
void
write() {printf(
"
%.10f %.10f\n
"
, x, y);}
}t, s1, s2, p[
5
], e[
20
];
double
r, rad[
5
];
int
cover[
5
];
sp
operator
*(
double
d,
const
sp &
v) {
return
sp(d*v.x, d*
v.y);
}
sp
operator
-(
const
sp &u,
const
sp &
v) {
return
sp(u.x-v.x, u.y-
v.y);
}
sp
operator
+(
const
sp &u,
const
sp &
v) {
return
sp(u.x+v.x, u.y+
v.y);
}
double
dot(
const
sp &u,
const
sp &
v) {
return
u.x*v.x+u.y*
v.y;
}
double
det(
const
sp &u,
const
sp &
v) {
return
u.x*v.y-u.y*
v.x;
}
double
dis(
const
sp &u,
const
sp &
v) {
double
dx = u.x-
v.x;
double
dy = u.y-
v.y;
return
sqrt(dx*dx+dy*
dy);
}
double
dissqr(
const
sp &u,
const
sp &
v) {
double
dx = u.x-
v.x;
double
dy = u.y-
v.y;
return
dx*dx+dy*
dy;
}
void
write(sp u, sp v) {
puts(
"
Now we have enough power
"
);
u.write(); v.write();
}
bool
cirint(
const
sp &u,
double
ru,
const
sp &v,
double
rv, sp &a, sp &
b) {
double
d =
dis(u, v);
if
(sgn(d-(ru+rv))>
0
|| sgn(d-fabs(ru-rv))<=
0
)
return
0
;
sp c
= u-
v;
double
ca, sa, cb, sb, csum, ssum;
ca
= c.x/d, sa = c.y/
d;
cb
= (rv*rv+d*d-ru*ru)/(
2
*rv*d), sb = sqrt(
1
-cb*
cb);
csum
= ca*cb-sa*
sb;
ssum
= sa*cb+sb*
ca;
a
= sp(rv*csum, rv*
ssum);
a
= a+
v;
sb
= -
sb;
csum
= ca*cb-sa*
sb;
ssum
= sa*cb+sb*
ca;
b
= sp(rv*csum, rv*
ssum);
b
= b+
v;
return
1
;
}
bool
cu(
int
N, sp p[],
double
r[], sp &
res) {
int
i, j, k;
memset(cover,
0
,
sizeof
cover);
for
(i =
0
; i < N; ++
i)
for
(j =
0
; j < N; ++
j) {
double
rd = r[i]-
r[j];
if
(i!=j && sgn(rd)>
0
&& sgn(dis(p[i], p[j])-rd)<=
0
)
cover[j]
++
;
}
sp a, b;
for
(i =
0
; i < N; ++
i) {
int
ecnt =
0
;
e[ecnt
++] = sp(p[i].x-r[i], p[i].y, -PI,
1
);
e[ecnt
++] = sp(p[i].x-r[i], p[i].y, PI, -
1
);
for
(j =
0
; j < N; ++
j) {
if
(j==i)
continue
;
if
(cirint(p[i], r[i], p[j], r[j], a, b)) {
e[ecnt
++] = sp(a.x, a.y, atan2(a.y-p[i].y, a.x-p[i].x),
1
);
e[ecnt
++] = sp(b.x, b.y, atan2(b.y-p[i].y, b.x-p[i].x), -
1
);
if
(sgn(e[ecnt-
2
].pa-e[ecnt-
1
].pa)>
0
) {
e[
0
].cnt++
;
e[
1
].cnt--
;
}
}
}
sort(e, e
+
ecnt);
int
cnt = e[
0
].cnt;
for
(j =
1
; j < ecnt; ++
j) {
if
(cover[i]+cnt==
N) {
double
a = (e[j].pa+e[j-
1
].pa)/
2
;
res
= p[i]+sp(r[i]*cos(a), r[i]*
sin(a));
return
1
;
}
cnt
+=
e[j].cnt;
}
}
return
0
;
}
bool
solve(
const
sp &o,
const
sp &u,
const
sp &v,
double
r) {
p[
0
] = o, rad[
0
] = r+
r;
p[
1
] = u; p[
2
] = v; rad[
1
] = rad[
2
] =
r;
sp a, b;
if
(cu(
3
, p, rad, a)) {
b
=
0.5
*(a-
o);
b
= o+
b;
write(a, b);
return
1
;
}
else
return
0
;
}
int
main() {
cin
>>
r;
t.read(); s1.read(); s2.read();
sp a, b, c, d;
if
(cirint(s1, r, s2, r, a, b)) {
if
(solve(t, s1, s2, r))
return
0
;
}
else
{
if
(cirint(s1, r, t, r, a, b) &&
cirint(s2, r, t, r, c, d)) {
write(a, c);
return
0
;
}
else
{
if
(solve(s1, t, s2, r))
return
0
;
if
(solve(s2, t, s1, r))
return
0
;
}
}
puts(
"
Death
"
);
return
0
;
}
顺便贴圆交模板,这回的应该没问题了。
#include <cmath>
#include
<algorithm>
using
namespace
std;
const
double
PI = acos(-
1.0
);
const
double
EPS = 1e-
8
;
int
sgn(
double
d) {
if
(fabs(d)<EPS)
return
0
;
return
d>
0
?
1
:-
1
;
}
struct
sp {
double
x, y;
double
pa;
int
cnt;
sp() {}
sp(
double
a,
double
b): x(a), y(b) {}
sp(
double
a,
double
b,
double
c,
int
d): x(a), y(b), pa(c), cnt(d) {}
bool
operator
<(
const
sp &rhs)
const
{
/*
需要处理单点的情况时
if (sgn(pa-rhs.pa)==0) return cnt>rhs.cnt;
*/
return
pa<
rhs.pa;
}
void
read() {scanf(
"
%lf%lf
"
, &x, &
y);}
void
write() {printf(
"
%lf %lf\n
"
, x, y);}
};
sp
operator
+(
const
sp &u,
const
sp &
v) {
return
sp(u.x+v.x, u.y+
v.y);
}
sp
operator
-(
const
sp &u,
const
sp &
v) {
return
sp(u.x-v.x, u.y-
v.y);
}
double
det(
const
sp &u,
const
sp &
v) {
return
u.x*v.y-v.x*
u.y;
}
double
dir(
const
sp &p,
const
sp &u,
const
sp &
v) {
return
det(u-p, v-
p);
}
double
dis(
const
sp &u,
const
sp &
v) {
double
dx = u.x-
v.x;
double
dy = u.y-
v.y;
return
sqrt(dx*dx+dy*
dy);
}
//
计算两圆交点
//
输入圆心坐标和半径
//
返回两圆是否相交(相切不算)
//
如果相交,交点返回在a和b(从a到b为u的交弧)
bool
cirint(
const
sp &u,
double
ru,
const
sp &v,
double
rv, sp &a, sp &
b) {
double
d =
dis(u, v);
/*
需要处理单点的情况时 覆盖的情况在这里不统计
if (sgn(d-(ru+rv))>0 || sgn(d-fabs(ru-rv))<=0) return 0;
*/
if
(sgn(d-(ru+rv))>=
0
|| sgn(d-fabs(ru-rv))<=
0
)
return
0
;
sp c
= u-
v;
double
ca, sa, cb, sb, csum, ssum;
ca
= c.x/d, sa = c.y/
d;
cb
= (rv*rv+d*d-ru*ru)/(
2
*rv*d), sb = sqrt(
1
-cb*
cb);
csum
= ca*cb-sa*
sb;
ssum
= sa*cb+sb*
ca;
a
= sp(rv*csum, rv*
ssum);
a
= a+
v;
sb
= -
sb;
csum
= ca*cb-sa*
sb;
ssum
= sa*cb+sb*
ca;
b
= sp(rv*csum, rv*
ssum);
b
= b+
v;
return
1
;
}
sp e[
222
];
int
cover[
111
];
//
求圆并
//
输入点数N,圆心数组p,半径数组r,答案数组s
//
s[i]表示被至少i个圆覆盖的面积(最普通的圆并就是s[1])
void
circle_union(
int
N, sp p[],
double
r[],
double
s[]) {
int
i, j, k;
memset(cover,
0
,
sizeof
cover);
for
(i =
0
; i < N; ++
i)
for
(j =
0
; j < N; ++
j) {
double
rd = r[i]-
r[j];
//
覆盖的情况在这里统计
if
(i!=j && sgn(rd)>
0
&& sgn(dis(p[i], p[j])-rd)<=
0
)
cover[j]
++
;
}
for
(i =
0
; i < N; ++
i) {
int
ecnt =
0
;
e[ecnt
++] = sp(p[i].x-r[i], p[i].y, -PI,
1
);
e[ecnt
++] = sp(p[i].x-r[i], p[i].y, PI, -
1
);
for
(j =
0
; j < N; ++
j) {
if
(j==i)
continue
;
if
(cirint(p[i], r[i], p[j], r[j], a, b)) {
e[ecnt
++] = sp(a.x, a.y, atan2(a.y-p[i].y, a.x-p[i].x),
1
);
e[ecnt
++] = sp(b.x, b.y, atan2(b.y-p[i].y, b.x-p[i].x), -
1
);
if
(sgn(e[ecnt-
2
].pa-e[ecnt-
1
].pa)>
0
) {
e[
0
].cnt++
;
e[
1
].cnt--
;
}
}
}
sort(e, e
+
ecnt);
int
cnt = e[
0
].cnt;
for
(j =
1
; j < ecnt; ++
j) {
double
pad = e[j].pa-e[j-
1
].pa;
s[cover[i]
+cnt] += (det(e[j-
1
], e[j])+r[i]*r[i]*(pad-sin(pad)))/
2
;
cnt
+=
e[j].cnt;
}
}
}

