【HAOI2008】bzoj1043 下落的圆盘

234 篇文章 0 订阅
177 篇文章 1 订阅

Description

  有n个圆盘从天而降,后面落下的可以盖住前面的。求最后形成的封闭区域的周长。看下面这副图, 所有的红 色线条的总长度即为所求. Input

  第一行为1个整数n,N<=1000 接下来n行每行3个实数,ri,xi,yi,表示下落时第i个圆盘的半径和圆心坐标.

Output

  最后的周长,保留三位小数

可以算是【uva1308 Viva Confetti】的升级版。
这回不能 O(n3) 枚举判断每一段小圆弧是否被覆盖了,可以把一个圆和在他上面的圆交出的所有圆弧找出来,找到这些弧的并集的补集就是这个圆露出的部分。求并集的时候可以转化为序列上的问题,做法就很多了。

#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const double eps=1e-8,pi=acos(-1);
int dcmp(double x)
{
    if (x>eps) return 1;
    if (fabs(x)<eps) return 0;
    return -1;
}
struct Vector
{
    double x,y;
    void rd()
    {
        scanf("%lf%lf",&x,&y);
    }
    Vector operator + (const Vector &v) const
    {
        return (Vector){x+v.x,y+v.y};
    }
    Vector operator - (const Vector &v) const
    {
        return (Vector){x-v.x,y-v.y};
    }
    Vector operator * (const int &k) const
    {
        return (Vector){x*k,y*k};
    }
    Vector operator / (const int &k) const
    {
        return (Vector){x/k,y/k};
    }
};
typedef Vector Point;
double dot(Vector v,Vector u)
{
    return v.x*u.x+v.y*u.y;
}
double cross(Vector v,Vector u)
{
    return v.x*u.y-v.y*u.x;
}
double length(Vector v)
{
    return sqrt(dot(v,v));
}
double dis(Point p,Point q)
{
    return length(p-q);
}
double convert(double a)
{
    while (dcmp(a)==-1) a+=2*pi;
    while (dcmp(a-2*pi)>=0) a-=2*pi;
    return a;
}
struct Circle
{
    Point o;
    double r;
    void rd()
    {
        scanf("%lf",&r);
        o.rd();
    }
}a[1010];
bool in(Circle c1,Circle c2)
{
    return dcmp(dis(c1.o,c2.o)-(c2.r-c1.r))<=0;
}
vector<pair<double,int> > ang;
vector<pair<double,int> >::iterator it;
void getintersection(Circle c1,Circle c2)
{
    double d=dis(c1.o,c2.o);
    if (dcmp(d-(c1.r+c2.r))>=0||dcmp(d-(c1.r-c2.r))<=0) return;
    double a1=atan2(c2.o.y-c1.o.y,c2.o.x-c1.o.x),a2=acos((c1.r*c1.r+d*d-c2.r*c2.r)/(2*c1.r*d));
    double s=convert(a1-a2),t=convert(a1+a2);
    if (dcmp(s-t)==1)
    {
        ang.push_back(make_pair(0,1));
        ang.push_back(make_pair(t,-1));
        ang.push_back(make_pair(s,1));
        ang.push_back(make_pair(2*pi,-1));
    }
    else
    {
        ang.push_back(make_pair(s,1));
        ang.push_back(make_pair(t,-1));
    }
}
double count()
{
    double ret=0;
    int cnt=0;
    sort(ang.begin(),ang.end());
    if (ang.empty()) return 0;
    for (it=ang.begin();(it+1)!=ang.end();it++)
    {
        cnt+=(*it).second;
        if (cnt) ret+=(*(it+1)).first-(*it).first;
    }
    return ret;
}
void check()
{
    for (it=ang.begin();it!=ang.end();it++)
        printf("%.3f %.3f\n",(*it).first,(*it).second);
}
int n;
int main()
{
    /*freopen("disc.in","r",stdin);
    freopen("disc.out","w",stdout);*/
    int ok;
    double ans=0;
    scanf("%d",&n);
    for (int i=1;i<=n;i++) a[i].rd();
    for (int i=1;i<=n;i++)
    {
        ang.clear();
        ok=1;
        for (int j=i+1;j<=n&&ok;j++)
        {
            if (in(a[i],a[j])) ok=0;
            else getintersection(a[i],a[j]);
        }
        if (ok) ans+=a[i].r*(2*pi-count());
        /*if (i==4) check();
        if (dcmp(ans)==-1)
        {
            printf("%d\n",i);
            break;
        }*/
    }
    printf("%.3f\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值