1. 程式人生 > >HDU 6097 Mindis(圓的反演)

HDU 6097 Mindis(圓的反演)

傳送門

題意:

有一個圓,圓心是(0,0),半徑是r,有兩個到圓心距離相等的點分佈在圓上或圓內,問在圓上找一點使得這點到那兩個點的距離和,輸出最小的距離。



思路:

首先題目的規模達到了500000,在3s內跑完,如果用三分的話,精度達到1e-6,一個數據大約找50遍,這樣複雜度就爆炸了。
最好的方法是一組資料由一個式子直接得出來,這樣複雜度O(1),還可以跑完。


接下來推公式。


程式碼:

#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <math.h>
using namespace std;
const double eps=1e-8;
typedef pair<double, double>point;

double Dist(const point &P1,const point &P2)
{
    double dx=P1.first-P2.first;
    double dy=P1.second-P2.second;
    return sqrt(dx*dx+dy*dy);
}

int main ()
{
    point P1,P2;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        double R;
        scanf("%lf",&R);
        scanf("%lf %lf",&P1.first,&P1.second);
        scanf("%lf %lf",&P2.first,&P2.second);
        double C=Dist(P1,P2)/2.0;
        double D=Dist(point(0,0),P1);
        if(D<eps)
        {
            printf("%.7f\n",R*2.0);
            continue;
        }
        double d=sqrt(D*D-C*C);
        double a=C*R/D;
        double y=d*(a*a-C*C)/(C*C);
        if(y<R-d)
        {
            printf("%.7f\n",a*2.0);
            continue;
        }
        printf("%.7f\n",sqrt(C*C+(R-d)*(R-d))*2.0);
    }
    return 0;
}

附上三分寫的超時的程式碼(以後哪天突然開竅了說不定就不T了):
#include <math.h>
#include <stdio.h>
#include <iostream>
using namespace std;
const double eps=1e-7;
double r;
double xx1,xx2,yy1,yy2;
template <class T>
inline bool scan_d(T &ret)
{
    char c;
    int sgn;
    if (c = getchar(), c == EOF)
    {
        return 0; //EOF
    }
    while (c != '-' && (c < '0' || c > '9'))
    {
        c = getchar();
    }
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9')
    {
        ret = ret * 10 + (c - '0');
    }
    ret *= sgn;
    return 1;
}

double f1(double x)//關係表示式
{
    double y=sqrt(r*r-x*x);
    double ans=sqrt((x-xx1)*(x-xx1)+(y-yy1)*(y-yy1));
    double ans2=sqrt((x-xx2)*(x-xx2)+(y-yy2)*(y-yy2));
    return ans+ans2;

}
double sanfen1(double l,double r)
{
    int aa=0;
    while(r-l>eps)        //精度自行設定
    {aa++;
         double ll=(2*l+r)/3;
        double rr=(l+2*r)/3;
        double ans1=f1(ll);
        double ans2=f1(rr);
        if(ans1>ans2)            //符號視情況而定
            l=ll;
        else
            r=rr;
    } //cout<<"aa="<<aa<<endl;
    return f1(l);

}

double f2(double x)//關係表示式
{
    double y=-sqrt(r*r-x*x);
    double ans=sqrt((x-xx1)*(x-xx1)+(y-yy1)*(y-yy1));
     double ans2=sqrt((x-xx2)*(x-xx2)+(y-yy2)*(y-yy2));
    return ans+ans2;

}
double sanfen2(double l,double r)
{
    int aa=0;
    while(r-l>eps)        //精度自行設定
    {aa++;
         double ll=(2*l+r)/3;
        double rr=(l+2*r)/3;
        double ans1=f2(ll);
        double ans2=f2(rr);
        if(ans1>ans2)            //符號視情況而定
            l=ll;
        else
            r=rr;
    } //cout<<"aa="<<aa<<endl;
    return f2(l);
}

int main()
{

    int t;

    scan_d(t);
    while(t--)
    {
        scan_d(r);
        scan_d(xx1);
        scan_d(yy1);
        scan_d(xx2);
        scan_d(yy2);

        double ans=0;
        if(xx1*xx1+yy1*yy1==r*r)
        {
            ans=sqrt((xx2-xx1)*(xx2-xx1)+(yy2-yy1)*(yy2-yy1));
        }
        else if(xx1==-xx2&&yy1==-yy2)
            ans=2*r;
        else if(yy1>=0&&yy2>=0)
            ans=sanfen1(-r,r);
            else if(yy1<0&&yy2<0)
            ans=sanfen2(-r,r);
            else
        ans=min(sanfen1(-r,r),sanfen2(-r,r));
        printf("%.7f\n",ans);
    }
    return 0;
}