1. 程式人生 > >bzoj1043[HAOI2008]下落的圓盤 計算幾何

bzoj1043[HAOI2008]下落的圓盤 計算幾何

bbs || truct content bsp reg images 枚舉 problem

1043: [HAOI2008]下落的圓盤

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 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

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 }

bzoj1043[HAOI2008]下落的圓盤 計算幾何