Bzoj1313 [HAOI2008]下落的圆盘
有 n 个圆盘从天而降,后面落下的可以盖住前面的。最后按掉下的顺序,在平面上依次测得每个圆盘的圆心和半径,问下落完成后从上往下看,整个图形的周长是多少,即你可以看到的圆盘的轮廓的圆盘的轮廓总长.例如下图的黑色线条的总长度即为所求。
【输入格式】
第一行为1个整数n
接下来n行每行3个实数,ri,xi,yi,表示下落时第i个圆盘的半径和圆心坐标.
【输出格式】
仅一个实数,表示所求的总周长,答案保留3位小数.
【样例输入】
2 1 0 0 1 1 0
【样例输出】
10.472
【提示】
30%的数据,n<=10
100%的数据,n<=1000
数学问题 计算几何
用余弦定理和三角函数可以计算出两圆相交部分的弧长。
对于每个圆,计算它和在它之后落下的所有圆的角。记录相交部分的弧对应的圆心角范围。将“圆心角”区间离散到0~2pi的数轴上,做线段覆盖。
知道了未被覆盖的角总共有多大,就能算出该圆未被覆盖的弧有多长。
注意如果求出的覆盖部分圆心角范围超出了0~2pi,要变换到0~2pi范围内
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #include<vector> 7 using namespace std; 8 const double pi=acos(-1.0); 9 const double eps=1e-7; 10 const int mxn=1010; 11 struct point{ 12 double x,y; 13 point operator + (point b){return (point){x+b.x,y+b.y};} 14 point operator - (point b){return (point){x-b.x,y-b.y};} 15 double operator * (point b){return x*b.x+y*b.y;} 16 }; 17 inline double Cross(point a,point b){ 18 return a.x*b.y-a.y*b.x; 19 } 20 inline double dist(point a,point b){ 21 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); 22 } 23 inline double Len(point a){return sqrt(a*a);} 24 struct cir{ 25 double x,y; 26 double r; 27 point operator + (cir b){return (point){x+b.x,y+b.y};} 28 point operator - (cir b){return (point){x-b.x,y-b.y};} 29 }c[mxn]; 30 inline bool cover(cir a,cir b){ 31 return (a.r>=b.r+Len(a-b)); 32 } 33 struct line{ 34 double l,r; 35 bool operator < (line b)const{ 36 return (l<b.l)|| (l==b.l && r<b.r); 37 } 38 }; 39 line CX(cir a,cir b){ 40 double dis=Len(b-a); 41 double angle=acos((a.r*a.r+dis*dis-b.r*b.r)/(2*a.r*dis)); 42 double deg=atan2(a.x-b.x,a.y-b.y);//统一旋转pi角度,保证在-2pi~2pi范围内 43 return (line){deg-angle,deg+angle}; 44 /* double t=(a.r*a.r+dis*dis-b.r*b.r)/(2*dis); 45 double st=atan2(a.x-b.x,a.y-b.y); 46 double l=acos(t/a.r); 47 return (line){st-l,st+l};*/ 48 } 49 vector<line>ve; 50 int n; 51 double ans=0; 52 void calc(int x){ 53 for(int i=x+1;i<=n;i++){if(cover(c[i],c[x]))return;}//被完全覆盖 54 ve.clear(); 55 for(int i=x+1;i<=n;i++){ 56 if(cover(c[x],c[i]))continue;//完全覆盖 57 line tmp; 58 if(c[x].r+c[i].r>Len(c[i]-c[x])) tmp=CX(c[x],c[i]); 59 else continue; 60 if(tmp.l<0) tmp.l+=2*pi; 61 if(tmp.r<0) tmp.r+=2*pi; 62 if(tmp.l>tmp.r){//拆分 63 ve.push_back((line){0,tmp.r}); 64 ve.push_back((line){tmp.l,2*pi}); 65 } 66 else ve.push_back(tmp); 67 } 68 sort(ve.begin(),ve.end()); 69 // printf("mid\n"); 70 double now=0,ran=0; 71 for(int i=0;i<ve.size();i++){//线段覆盖 72 line tmp=ve[i]; 73 // printf("i:%d %.3f %.3f\n",i,tmp.l,tmp.r); 74 if(tmp.l>now){ran+=tmp.l-now;now=tmp.r;} 75 else now=max(now,tmp.r); 76 } 77 ran+=2*pi-now; 78 ans+=c[x].r*ran;//累加半径 79 return; 80 } 81 int main(){ 82 freopen("disc.in","r",stdin); 83 freopen("disc.out","w",stdout); 84 int i,j; 85 scanf("%d",&n); 86 for(i=1;i<=n;i++){ 87 scanf("%lf%lf%lf",&c[i].r,&c[i].x,&c[i].y); 88 } 89 for(i=n;i>=1;i--){ 90 calc(i); 91 // printf("ans:%.3f\n",ans); 92 } 93 printf("%.3f\n",ans); 94 return 0; 95 }
本文为博主原创文章,转载请注明出处。