2013 ACM/ICPC Asia Regional Changchun Online

hdu 4766

先二分答案,然后相当与判断n+1个圆是否有交,注意,只需要判断是否,所以就可以用nlogn的方法来判断。

总体复杂度为nlogn^2,所以跑起来还是很快的

nlogn判断n个圆是否有交是这样的,我们先锁定x的范围,也就是所有圆的右边界的最小值right,以及所有圆的左边界的最大值left

那么n个圆的公共部分肯定在left right之间,而且有一个很重要的性质,如果n个圆的公共部分的左右区间是L,R,假设我们当前枚举的答案是mid,如果x=mid这条直线与所有的圆没有公共部分,那么我们找两个圆与直线的公共部分不相交(其实就是上边界最小以及下边界最大的两个圆),判断这两个圆的公共部分在x=mid的哪一侧即可,其实就是满足二分性质啦~,具体可以看代码

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 1e-8;
const double pi = acos(-1.0);

struct Point {
    double x, y;
    Point() {
    }
    Point(double _x,double _y) {
        x = _x ;
        y = _y;
    }
    Point operator - (const Point& t) const {
        Point tmp;
        tmp.x = x - t.x;
        tmp.y = y - t.y;
        return tmp;
    }
    Point operator + (const Point& t) const {
        Point tmp;
        tmp.x = x + t.x;
        tmp.y = y + t.y;
        return tmp;
    }
    bool operator < (const Point &cmp) const {
        return x - cmp.x < -eps || (fabs(x-cmp.x)<eps && y -cmp.y < -eps);
    }
    bool operator == (const Point& t) const {
        return fabs(x-t.x) < eps && fabs(y-t.y) < eps;
    }
}GP;
struct Line {
    double a,b,c;
};
double PPdis(Point a,Point b)  {

    return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
double PL_dis(Point p,Line ln) {
    return fabs(ln.a*p.x+ln.b*p.y+ln.c) / sqrt(ln.a*ln.a+ln.b*ln.b);
}
bool Cir_Line(Point ct, double r, Line ln, Point& t1, Point& t2) {   // 直线与圆交点
    if ( PL_dis(ct, ln) > r + eps )
        return false;
    ln.c += ln.a*ct.x + ln.b*ct.y;
    if ( fabs(ln.b) < eps ) {
        t1.x = t2.x = -ln.c/ln.a;
        t1.y = sqrt(r*r - ln.c*ln.c/ln.a/ln.a);
        t2.y = -t1.y;
    } 
    else {
        double A, B, C;
        A = ln.a*ln.a + ln.b*ln.b;
        B = 2.0*ln.a*ln.c;
        C = ln.c*ln.c - ln.b*ln.b*r*r;
        t1.x = (-B - sqrt(B*B - 4.0*A*C))/2.0/A;
        t2.x = (-B + sqrt(B*B - 4.0*A*C))/2.0/A;
        t1.y = -t1.x*ln.a/ln.b - ln.c/ln.b;
        t2.y = -t2.x*ln.a/ln.b - ln.c/ln.b;
    }
    t1 = t1 + ct;
    t2 = t2 + ct;
    return true;
}
bool Cir_Cir(Point c1, double r1, Point c2, double r2, Point& t1, Point& t2) {  // 圆与圆交点
    double d = PPdis(c1, c2);
    if ( d > r1+r2+eps || d < fabs(r1-r2)-eps )
        return false;
    Line ln;
    ln.a = 2*(c1.x - c2.x);
    ln.b = 2*(c1.y - c2.y);
    ln.c = r1*r1 - r2*r2 - (c1.x*c1.x+c1.y*c1.y-c2.x*c2.x-c2.y*c2.y);
    Cir_Line(c1, r1, ln, t1, t2);
    return true;
}
double R[1010];
Point p[1010];
int n;
bool judge(double mid)
{
    double Left,Right;
    for(int i = 0; i <= n; i++) {
        if(i == 0) {
            Left = p[i].x - R[i];
            Right = p[i].x + R[i];
        } else{
            if(p[i].x-R[i] > Left) Left = p[i].x-R[i];
            if(p[i].x+R[i] < Right) Right = p[i].x+R[i];
        }
    }

    if(Left - Right > eps) return false;
    int step = 50;
    while(step--) {
        double mid = (Left + Right)*0.5;
        double low,high,uy,dy;
        int low_id,high_id;
        for(int i = 0; i <= n; i++) {
            double d = sqrt(R[i]*R[i]-(p[i].x-mid)*(p[i].x-mid));
            uy = p[i].y + d;
            dy = p[i].y - d;
            if(i == 0) {
                low_id = high_id = 0;
                low = dy; high = uy;
            } else {
                if(uy < high) high = uy,high_id = i;
                if(dy > low) low = dy,low_id = i;
            }
        }
        
        if(high - low > -eps) {
            return 1;
        }
        Point a,b;
        if(Cir_Cir(p[high_id],R[high_id],p[low_id],R[low_id],a,b)) {
            if((a.x+b.x)*0.5 < mid) {
                Right = mid;
            } else Left = mid;
        } else return false;
    }
    return false;
}
int main()
{
    Point p0;
    double d;
    while(scanf("%lf%lf",&p0.x,&p0.y)!=EOF) {
         scanf("%lf",&d);
         scanf("%d",&n);
         for(int i = 0; i < n; i++) {
             R[i] = d;
             scanf("%lf%lf",&p[i].x,&p[i].y);
         }
         p[n] = p0;
         double Left = 0, Right = 1e10, best = -1;
         int step = 50;
         while(step--) {
             double mid = (Left + Right)*0.5;
             R[n] = mid;
             if(judge(mid)) {
                 best = mid;
                 Right = mid;
             } else Left = mid;
         }
         if(best == -1) {
             puts("X");
         }else printf("%.2f\n",best);
    }
}


你可能感兴趣的:(2013 ACM/ICPC Asia Regional Changchun Online)