【HAOI2008】下落的圆盘
原题:
n<=1000,周长和坐标都是浮点数
原来认为的难题现在直接就切掉了,快乐(虽然找小错误找了很长时间)
因为已经有一定的数学知识和能力了,所以找到正解很轻松
果然数学是OI第一生产力呀
因为n不大,支持n^2,那么可以考虑其他所有圆盘对某个圆盘的覆盖情况
对于某个圆盘,以圆心为极点,水平向右为极轴建立极坐标系
接下来枚举在此圆之后落下的圆
然后用极坐标与直角坐标的转化可以得到另一个圆心的角度坐标
用余弦定理可以得到两圆交弧对应的张角
结合角度坐标得到交弧的角度坐标区间
如果区间跨过2pi或0,就拆成两个,坐标范围都转到[0,2pi]
然后求区间并,得到被覆盖的总角度
然后用弧长公式得到被覆盖的弧长
这道题一共用了3个集合知识:
极坐标,余弦定理和弧长公式
如果高四之前做的确是搞不出来的233
易错点:
1.注意判定两个半径和两圆心的连线能否构成三角形
总共有3种情况,不要漏掉
2.直角坐标转极坐标的时候注意α不是arccos(Δx/l)
arccos的范围是[0,pi],还需要判定Δy
就是这俩玩意我找了一个小时,计算几何还是细啊
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 double eps=1e-6; 7 double pi=acos(-1); 8 struct nds{double r,x,y;}a[1100]; 9 struct nd{double l,r;}q[2100]; int hd=0; 10 int n; 11 double ans=0; 12 double sqr(double x){ return x*x;} 13 double ds(int x,int y){ 14 return sqrt(sqr(a[x].x-a[y].x)+sqr(a[x].y-a[y].y)); 15 } 16 bool cmp(nd x,nd y){ return x.l<y.l;} 17 double cclt(int x){ 18 if(a[x].r<eps) return 0; 19 hd=0; 20 for(int i=n;i>x;--i)if(a[i].r>0 && a[x].r+a[i].r>ds(x,i) && a[x].r<a[i].r+ds(x,i)){ 21 //注意判断r1<r2+l!!! 22 if(ds(x,i)+a[x].r<a[i].r) return 0; 23 if(ds(x,i)<eps) continue; 24 double th=acos((sqr(a[x].r)+sqr(ds(x,i))-sqr(a[i].r))/(2.0*a[x].r*ds(x,i))); 25 double af=acos((a[i].x-a[x].x)/ds(x,i)); 26 if(a[i].y<a[x].y) af=2.0*pi-af; 27 //注意,直接对Δx求出的α范围是0到pi 28 if(af-th<0){ 29 q[++hd]=(nd){0,af+th}; 30 q[++hd]=(nd){af-th+2.0*pi,2.0*pi}; 31 } 32 else if(af+th>2*pi){ 33 q[++hd]=(nd){af-th,2.0*pi}; 34 q[++hd]=(nd){0,af+th-2.0*pi}; 35 } 36 else q[++hd]=(nd){af-th,af+th}; 37 } 38 sort(q+1,q+hd+1,cmp); 39 double l=0,r=0; 40 double bwl=0; 41 for(int i=1;i<=hd;++i){ 42 if(q[i].l<r) r=max(r,q[i].r); 43 else{ 44 bwl+=r-l; 45 l=q[i].l,r=q[i].r; 46 } 47 } 48 bwl+=r-l; 49 return (2.0*pi-bwl)*a[x].r; 50 } 51 int main(){ 52 cin>>n; 53 for(int i=1;i<=n;++i){ 54 scanf("%lf%lf%lf",&a[i].r,&a[i].x,&a[i].y); 55 } 56 for(int i=n;i>=1;--i){ 57 ans+=cclt(i); 58 } 59 printf("%.3lf\n",ans); 60 return 0; 61 }