bzoj1043 下落的圆盘
1043: [HAOI2008]下落的圆盘
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1587 Solved: 669
[Submit][Status][Discuss]
Description
有n个圆盘从天而降,后面落下的可以盖住前面的。求最后形成的封闭区域的周长。看下面这副图, 所有的红
色线条的总长度即为所求.
Input
第一行为1个整数n,N<=1000
接下来n行每行3个实数,ri,xi,yi,表示下落时第i个圆盘的半径和圆心坐标.
Output
最后的周长,保留三位小数
Sample Input
2
1 0 0
1 1 0
1 0 0
1 1 0
Sample Output
10.472
分析:一开始的思路是每增加一个圆盘,看它能覆盖到被计入答案的区域,答案减掉这个长度.这个想法似乎是可行的,但是维护每个圆盘哪些位置被覆盖了非常困难.
换个思路,考虑每个圆盘对答案的贡献:原周长-被后来的圆盘覆盖的周长.实际上后面一部分就是要求周长并,可以将圆拆成一条线段,规定一个起点和终点.但是这样的话还要求两个圆的交点,不好处理.一个比较好的方法是记录被覆盖部分的圆心角,放到[0,2π)这个区间里.利用两个圆心的极角和圆心角可以比较容易地计算.圆心角可以通过余弦定理得到.需要注意的是左右端点<0和≥2π的情况.
还要判断两圆是否是包含/相离关系.
最后怎么求周长并?先把弧度并给求出来,乘上半径就是了.具体方法是在左端点+1,右端点-1,用cnt统计+1,-1的和,如果cnt > 0,则ans -= 第i个弧度-第i-1个弧度.ans初始化为2π.(有点复杂,结合代码更好理解)
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const double pi = acos(-1.0),eps = 1e-8; int n,tot; double ans,sum; struct node { double r,x,y; } e[1010]; struct node2 { double x; int id; } p[10010]; double D(node a,node b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); } bool cmp(node2 a,node2 b) { return a.x < b.x; } int main() { scanf("%d",&n); for (int i = 1; i <= n; i++) scanf("%lf%lf%lf",&e[i].r,&e[i].x,&e[i].y); for (int i = 1; i <= n; i++) { bool flag = false; tot = 0; sum = 0.0; for (int j = i + 1; j <= n; j++) { double dist = D(e[i],e[j]); if (dist <= e[j].r - e[i].r) //完全包含了 { flag = 1; break; } if (dist > fabs(e[i].r - e[j].r) && dist <= e[i].r + e[j].r) //必须要相交不能分离 { double temp = acos((e[i].r * e[i].r + dist * dist - e[j].r * e[j].r) / (2 * e[i].r * dist)); double temp2 = atan2(e[j].y - e[i].y,e[j].x - e[i].x); double l = temp2 - temp,r = temp2 + temp; if (l < 0 && r < 0) { l += 2 * pi; r += 2 * pi; } if (l >= 0 && r <= 2 * pi) { p[++tot].x = l; p[tot].id = 1; p[++tot].x = r; p[tot].id = -1; } else if (l < 0) { p[++tot].x = l + 2 * pi; p[tot].id = 1; p[++tot].x = 2 * pi; p[tot].id = -1; p[++tot].x = 0; p[tot].id = 1; p[++tot].x = r; p[tot].id = -1; } else { p[++tot].x = l; p[tot].id = 1; p[++tot].x = 2 * pi; p[tot].id = -1; p[++tot].x = 0; p[tot].id = 1; p[++tot].x = r - 2 * pi; p[tot].id = -1; } } } if (flag) continue; ans += e[i].r * 2 * pi; if (!tot) continue; sort(p + 1,p + 1 + tot,cmp); for(int j = 1; j <= tot; j++) { if (sum && fabs(p[j].x - p[j-1].x) > eps) ans -= e[i].r * (p[j].x - p[j - 1].x); sum += p[j].id; } } printf("%.3lf\n",ans); return 0; }