bzoj1043 [HAOI2008]下落的圆盘

来源:互联网 发布:行知中学住宿条件 编辑:程序博客网 时间:2024/04/26 22:01

【题意】

n个圆依次下落,后面的圆覆盖前面的圆,求最终图形周长。


【数据范围】

n<=1000

【思路】

从后往前枚举所有的圆,对于当前圆,计算出后面每个圆对其覆盖的圆弧,排序求并集即可

atan2(y,x)=向量(x,y)与x轴正方向夹角

计算圆弧左右端点的方法:用atan2计算圆心相连所成向量的角度k,余弦定理计算圆弧端点和圆心的连线与两圆心连线之间的夹角α,则圆弧角度区间为[k-α,k+α]

【时间复杂度】

O(n^2 log n)

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 2010#define pi acos(-1)#define eps 1e-9using namespace std;struct aa{double l, r;}a[N];struct bb{double x, y, r;}b[N];int n, ff, l;double d, ans, x, k, alpha, L, R, now;double dis(int i, int j){    return sqrt((b[i].x-b[j].x)*(b[i].x-b[j].x)+(b[i].y-b[j].y)*(b[i].y-b[j].y));}bool cmp(aa a, aa b){return a.l<b.l;}int main(){    scanf("%d", &n);    for(int i=1; i<=n; i++)scanf("%lf%lf%lf", &b[i].r, &b[i].x, &b[i].y);    ans=0;    for(int i=n; i; i--){        ff=1;        for(int j=i+1; j<=n&&ff; j++){            d=dis(i, j);            if(b[i].r+d<=b[j].r)ff=0;        }        if(!ff)continue;        l=0;        for(int j=i+1; j<=n; j++){            d=dis(i, j); if(b[i].r<eps||d<eps)continue;            if(d<=abs(b[i].r-b[j].r)||d>=b[i].r+b[j].r)continue;            k=atan2(b[j].y-b[i].y, b[j].x-b[i].x); //atan2(y,x)=向量(x,y)与x轴正方向夹角             alpha=acos((b[i].r*b[i].r+d*d-b[j].r*b[j].r)/(2*b[i].r*d));            L=k-alpha; R=k+alpha; if(L<-pi)L+=2*pi; if(R>pi)R-=2*pi;            if(L<=R){a[++l].l=L; a[l].r=R;}            else{                a[++l].l=L; a[l].r=pi;                a[++l].l=-pi; a[l].r=R;            }        }        sort(a+1, a+1+l, cmp); now=-pi;        for(int j=1; j<=l; j++){            if(now<a[j].l)ans+=(a[j].l-now)*b[i].r;            now=max(now, a[j].r);        }        if(now<pi)ans+=(pi-now)*b[i].r;    }    printf("%.3f", ans);    return 0;}


0 0
原创粉丝点击