题目传送门
题目大意是计算几何, n n n 个圆在平面上编号大的圆将编号小的圆覆盖求最后所有没有被覆盖的圆的边缘的总长度。
在做这道题之前有几个前置知识。
极坐标系:在平面内由极点、极轴和极径组成的坐标系。
如:在平面上取一点 O O O 叫做极点,从 O O O 出发引一条射线 O x Ox Ox 称为极轴。通常规定角度取逆时针方向为正。
极角:在极坐标系中,平面上任何一点到极点的连线和极轴的夹角叫做极角。
那么,我们可以发现极角的大小为 0 ∼ 360 0\sim360 0∼360 度(考虑正角)。
极角可以进行排序,(由小到大的那种),那么对于平面上一个点 ( x , y ) (x,y) (x,y) 到极点的连线和极轴 x x x 的夹角大小为 atan2( y ÷ x y \div x y÷x)。
atan2 指的 t a n tan tan 的反函数是方位角。
atan2 比 atan 稳定,所以我们使用 atan2。
但是我们要求出极角来,这个返回的是方位角,如果当前角度为正,那么就是极角, 如果为负,我们需要将其加上 2 π 2\pi 2π,就变成极角了。
接下来就可以做这道题了,首先考虑两圆相交如何求夹角?
余弦定理和acos函数即可。 n 2 n^2 n2 求交,然后我们发交的地方只算一次,所以可以利用极角来做。
求出所有的极角之后,按极角排序,然后就是直线的覆盖问题。
注意一下覆盖弧度范围跨越 0 0 0 和 2 π 2\pi 2π 的处理。
#include <bits/stdc++.h>
#define N 1010
#define squ(x) ((x) * (x))
using namespace std;
const double pi = acos(-1);
struct data
{
double pl , pr;
bool operator<(const data &a)const {return pl < a.pl;}
}a[N << 1];
double x[N] , y[N] , r[N];
int tot;
int main()
{
int n , i , j;
double afa , beta , d , last , ans = 0;
scanf("%d" , &n);
for(i = 1 ; i <= n ; i ++ ) scanf("%lf%lf%lf" , &r[i] , &x[i] , &y[i]);
for(i = 1 ; i <= n ; i ++ )
{
ans += 2 * pi * r[i];
tot = 0;
for(j = i + 1 ; j <= n ; j ++ )
{
tot ++ , d = squ(x[i] - x[j]) + squ(y[i] - y[j]);
if(squ(r[i] + r[j]) <= d) a[tot].pl = a[tot].pr = 0;
else if(squ(r[i] - r[j]) >= d)
{
if(r[i] > r[j]) a[tot].pl = a[tot].pr = 0;
else a[tot].pl = 0 , a[tot].pr = 2 * pi;
}
else
{
afa = acos((r[i] * r[i] + d - r[j] * r[j]) / (2 * r[i] * sqrt(d)));
beta = atan2(y[j] - y[i] , x[j] - x[i]);
if(beta < 0) beta += 2 * pi;
a[tot].pl = beta - afa , a[tot].pr = beta + afa;
if(a[tot].pl < 0) tot ++ , a[tot].pl = a[tot - 1].pl + 2 * pi , a[tot - 1].pl = 0 , a[tot].pr = 2 * pi;
else if(a[tot].pr > 2 * pi) tot ++ , a[tot].pr = a[tot - 1].pr - 2 * pi , a[tot - 1].pr = 2 * pi , a[tot].pl = 0;
}
}
sort(a + 1 , a + tot + 1);
last = -1;
for(j = 1 ; j <= tot ; j ++ )
{
if(a[j].pr <= last) continue;
if(a[j].pl > last) ans -= (a[j].pr - a[j].pl) * r[i];
else ans -= (a[j].pr - last) * r[i];
last = a[j].pr;
}
}
printf("%.3lf\n" , ans);
return 0;//完美收官QwQ
}