1043: [HAOI2008]下落的圆盘
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1598 Solved: 676
[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
HINT
Source
可以考虑后放的盘子对先放的盘子造成的覆盖影响
枚举每个盘,这个盘贡献的答案就是它的周长减去被后面的盘所覆盖的部分
圆和圆相交部分长度可以转化成线段覆盖问题来计算:算出中轴线的角度x,再计算圆心到2交点的角度y,
圆上被覆盖的弧度区间就是[x-y,x+y]
几个圆与这个圆相交,把相交覆盖的弧转化成圆上的线段,做一个线段覆盖问题就可以解决
1 #include<bits/stdc++.h> 2 #define reg register 3 #define N 1005 4 using namespace std; 5 int n,tp;double x[N],y[N],r[N],ans;const double pi=acos(-1);struct node{double l,r;}q[N<<1]; 6 double dis(int i,int j){return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));} 7 bool con(int i,int j){return r[i]>=r[j]+dis(i,j);} 8 bool cmp(node a,node b){return a.l<b.l;} 9 void push(int i,int j){ 10 double d,k,t,mv; 11 d=dis(i,j);k=atan2(y[j]-y[i],x[j]-x[i]); 12 t=(d*d+r[i]*r[i]-r[j]*r[j])/2/d; 13 mv=acos(t/r[i]); 14 q[++tp]=(node){k-mv,k+mv}; 15 } 16 double calc(int p){ 17 tp=0; 18 for(reg int i=p+1;i<=n;i++) 19 if(con(i,p))return 0; 20 for(int i=p+1;i<=n;i++){ 21 if(con(p,i)||dis(i,p)>r[i]+r[p])continue; 22 push(p,i); 23 } 24 for(reg int i=1;i<=tp;i++){ 25 if(q[i].l<0)q[i].l+=2*pi; 26 if(q[i].r<0)q[i].r+=2*pi; 27 if(q[i].l>q[i].r){ 28 q[++tp]=(node){0,q[i].r}; 29 q[i].r=2*pi; 30 } 31 } 32 sort(q+1,q+1+tp,cmp); 33 double cov=0,mx=q[1].l; 34 for(reg int i=1;i<=tp;i++){ 35 mx=max(mx,q[i].l); 36 if(q[i].r<=mx)continue; 37 cov+=q[i].r-mx;mx=q[i].r; 38 } 39 double ret=r[p]*(2*pi-cov); 40 return ret; 41 } 42 43 int main(){ 44 scanf("%d",&n); 45 for(reg int i=1;i<=n;i++) 46 scanf("%lf%lf%lf",&r[i],&x[i],&y[i]); 47 for(reg int i=1;i<=n;i++)ans+=calc(i); 48 printf("%.3lf\n",ans); 49 return 0; 50 }