2017 ECNA Regional Contest B:Craters (圆的凸包)

传送门

题解:
做出圆的两两公切点,丢掉所有在某个圆内部的点,之后做凸包即可。计算长度时判断是否在一个圆上。

注意圆先要去重,以及计算两点在圆上的长度先要判断是优弧还是劣弧。

#include 
typedef long double LD;
using namespace std;
inline int rd() {
    char ch=getchar(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}
const int N=1e6+50;
const LD PI=acos(-1.0);
const LD PI2=acos(-1.0)*2;
const LD rang=PI/2;
const LD eps=1e-8;
inline int sgn(LD x) {return (x>eps)-(x<-eps);}
inline LD fix(LD x) {
    while(x<-PI) x+=PI2;
    while(x>PI) x-=PI2;
    return x;
}
int n,tot,cont[N];
struct P {
    LD x,y;
    P(LD x=0,LD y=0):x(x),y(y){}
    friend inline P operator +(const P &a,const P &b) {return P(a.x+b.x,a.y+b.y);}
    friend inline P operator -(const P &a,const P &b) {return P(a.x-b.x,a.y-b.y);}
    friend inline LD operator *(const P &a,const P &b) {return a.x*b.y-a.y*b.x;}
    inline LD dis() {return sqrt(x*x+y*y);}
    inline P rev(LD o) {
        LD sn=sin(o), cs=cos(o);
        return P(x*cs-y*sn,x*sn+y*cs);
    }
    inline P set(LD R) {
        LD len=dis();
        return P(x/len*R,y/len*R);
    }
}p[N];
struct C {
    P a; LD R;
    friend inline LD dis(const C &a,const C &b) {return (b.a-a.a).dis();}
    friend inline bool operator <(const C &a,const C &b) {
        if(sgn(a.a.x-b.a.x)) return sgn(a.a.x-b.a.x)<0;
        else if(sgn(a.a.y-b.a.y)) return sgn(a.a.y-b.a.y)<0;
        else return a.Rfriend inline bool operator ==(const C &a,const C &b) {
        return !sgn(a.a.x-b.a.x) && !sgn(a.a.y-b.a.y) && !sgn(a.R-b.R);
    }
}cir[N];
inline bool in(C a,C b) {
    if(a.Rreturn false;
    LD dist=dis(a,b);
    if(dist>a.R) return false;
    return sgn(dist-a.R+b.R)<=0;
}
inline void calc_tangent_line(C a,C b) {
    if(a.R>b.R) swap(a,b);
    if(in(a,b)) return;
}
inline void calc_tangle_point(C a,C b) {
    if(a.Rasin((a.R-b.R)/dist));
    if(ang>rang) ang=PI-ang;
    LD ang2=rang-ang;
    P tar=a.a-b.a,t;
    t=tar.rev(-ang-rang);
    p[++tot]=b.a+t.set(b.R);
    t=tar.rev(ang+rang);
    p[++tot]=b.a+t.set(b.R);
    tar=b.a-a.a;
    t=tar.rev(ang2);
    p[++tot]=a.a+t.set(a.R);
    t=tar.rev(-ang2);
    p[++tot]=a.a+t.set(a.R);
}
inline bool cmp(const P &a,const P &b) {
    LD t=(a-p[1])*(b-p[1]);
    if(sgn(t)) return sgn(t)>0;
    return (a-p[1]).dis()<(b-p[1]).dis();
}
inline int build_convex() {
    int c=1,t;
    for(int i=1;i<=tot;i++) 
        if(i==1 || sgn(p[i].x-p[t].x)<0 || (!sgn(p[i].x-p[t].x) && sgn(p[i].y-p[t].y)<0)) t=i;
    if(t!=1) swap(p[1],p[t]);
    sort(p+2,p+tot+1,cmp);
    for(int i=2;i<=tot;i++) {
        while(c>=2 && sgn((p[i]-p[c-1])*(p[c]-p[c-1]))>=0 ) --c;
        p[++c]=p[i];
    } 
    return c;
}
inline int same_cir(const P &a,const P &b) {
    for(int i=1;i<=n;i++) 
        if(!sgn((a-cir[i].a).dis()-cir[i].R) && !sgn((b-cir[i].a).dis()-cir[i].R)) return i;
    return 0;
}
inline LD on_cir(const P &a,const P &b,const C &c) {
    LD l=(b-a).dis();
    LD R=c.R;
    if(l>R) l-=eps;
    LD ang=fix(acos((2*R*R-l*l)/(2*R*R)));
    if(ang<0) ang=-ang;
    if((a-c.a)*(b-c.a)<0) ang=PI2-ang;
    return ang*R;
}
int main() {
    n=rd();
    for(int i=1;i<=n;i++) 
        cir[i].a.x=rd(), cir[i].a.y=rd(), cir[i].R=rd()+10;
    sort(cir+1,cir+n+1);
    n=unique(cir+1,cir+n+1)-cir-1;
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=n;j++) 
            if(i!=j && in(cir[i],cir[j])) cont[j]=1;
    int t=n; n=0;
    for(int i=1;i<=t;i++) 
        if(!cont[i]) cir[++n]=cir[i];
        else cont[i]=0;
    if(n==1) {printf("%.7f",(double)(PI*2*cir[1].R));}
    else {
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                calc_tangle_point(cir[i],cir[j]);
        for(int i=1;i<=tot;i++)
            for(int j=1;j<=n;j++)
                if(sgn((p[i]-cir[j].a).dis()-cir[j].R)<0) cont[i]=1;
        t=tot; tot=0;
        for(int i=1;i<=t;i++)
            if(!cont[i]) p[++tot]=p[i];
        tot=build_convex();
        p[tot+1]=p[1];
        LD rs=0;
        for(int i=1;i<=tot;i++) {
            int t=same_cir(p[i],p[i+1]);
            if(t) rs+=on_cir(p[i],p[i+1],cir[t]);
            else rs+=(p[i]-p[i+1]).dis();
        }
        printf("%.7f",(double)rs);
    }
}

你可能感兴趣的:(凸包)