2017多校第6场 HDU 6097 Mindis 计算几何,圆的反演

Posted Lsxxxxxxxxxxxxx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017多校第6场 HDU 6097 Mindis 计算几何,圆的反演相关的知识,希望对你有一定的参考价值。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6097

题意:有一个圆心在原点的圆,给定圆的半径,给定P、Q两点坐标(PO=QO,P、Q不在圆外),取圆上一点D,求PD+QD的最小值。

解法:圆的反演。

很不幸不总是中垂线上的点取到最小值,考虑点在圆上的极端情况。

做P点关于圆的反演点P‘,OPD与ODP‘相似,相似比是|OP| : r。

Q点同理。

极小化PD+QD可以转化为极小化P‘D+Q‘D。

当P‘Q‘与圆有交点时,答案为两点距离,否则最优值在中垂线上取到。

时间复杂度 O(1)

也有代数做法,结论相同。

优秀的黄金分割三分应该也是可以卡过的。

分析可以看这个博客:http://blog.csdn.net/qq_34845082/article/details/77099332

 

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
struct FastIO
{
    static const int S = 1310720;
    int wpos;
    char wbuf[S];
    FastIO() : wpos(0) {}
    inline int xchar()
    {
        static char buf[S];
        static int len = 0, pos = 0;
        if(pos == len)
            pos = 0, len = fread(buf, 1, S, stdin);
        if(pos == len)
            exit(0);
        return buf[pos ++];
    }
    inline unsigned long long xuint()
    {
        int c = xchar();
        unsigned long long x = 0;
        while(c <= 32)
            c = xchar();
        for(; ‘0‘ <= c && c <= ‘9‘; c = xchar())
            x = x * 10 + c - ‘0‘;
        return x;
    }
    inline long long xint()
    {
        long long s = 1;
        int c = xchar(), x = 0;
        while(c <= 32)
            c = xchar();
        if(c == ‘-‘)
            s = -1, c = xchar();
        for(; ‘0‘ <= c && c <= ‘9‘; c = xchar())
            x = x * 10 + c - ‘0‘;
        return x * s;
    }
    inline void xstring(char *s)
    {
        int c = xchar();
        while(c <= 32)
            c = xchar();
        for(; c > 32; c = xchar())
            * s++ = c;
        *s = 0;
    }
    inline double xdouble()
    {
        bool sign = 0;
        char ch = xchar();
        double x = 0;
        while(ch <= 32)
            ch = xchar();
        if(ch == ‘-‘)
            sign = 1, ch = xchar();
        for(; ‘0‘ <= ch && ch <= ‘9‘; ch = xchar())
            x = x * 10 + ch - ‘0‘;
        if(ch == ‘.‘)
        {
            double tmp = 1;
            ch = xchar();
            for(; ch >= ‘0‘ && ch <= ‘9‘; ch = xchar())
                tmp /= 10.0, x += tmp * (ch - ‘0‘);
        }
        if(sign)
            x = -x;
        return x;
    }
    inline void wchar(int x)
    {
        if(wpos == S)
            fwrite(wbuf, 1, S, stdout), wpos = 0;
        wbuf[wpos ++] = x;
    }
    inline void wint(long long x)
    {
        if(x < 0)
            wchar(‘-‘), x = -x;
        char s[24];
        int n = 0;
        while(x || !n)
            s[n ++] = ‘0‘ + x % 10, x /= 10;
        while(n--)
            wchar(s[n]);
    }
    inline void wstring(const char *s)
    {
        while(*s)
            wchar(*s++);
    }
    inline void wdouble(double x, int y = 8)
    {
        static long long mul[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000LL, 100000000000LL, 1000000000000LL, 10000000000000LL, 100000000000000LL, 1000000000000000LL, 10000000000000000LL, 100000000000000000LL};
        if(x < -1e-12)
            wchar(‘-‘), x = -x;
        x *= mul[y];
        long long x1 = (long long) floorl(x);
        if(x - floor(x) >= 0.5)
            ++x1;
        long long x2 = x1 / mul[y], x3 = x1 - x2 * mul[y];
        wint(x2);
        if(y > 0)
        {
            wchar(‘.‘);
            for(size_t i = 1; i < y && x3 * mul[i] < mul[y]; wchar(‘0‘), ++i);
            wint(x3);
        }
    }
    ~FastIO()
    {
        if(wpos)
            fwrite(wbuf, 1, wpos, stdout), wpos = 0;
    }
} io;

const double eps = 1e-8;
double getDis(double x, double y){
    return sqrt(x*x+y*y);
}
double r,x1,y1,x2,y2;

int main()
{
    int T = io.xint();
    while(T--)
    {
        r = io.xdouble();
        x1 = io.xdouble();
        y1 = io.xdouble();
        x2 = io.xdouble();
        y2 = io.xdouble();
        double d0 = getDis(x1, y1);
        if(fabs(d0)<=eps){//p和q和原点重合
            printf("%.8f\n", 2*r);
            continue;
        }
        double k = r*r/d0/d0;//用这个比例确定p和q的反演点
        double x3=x1*k, x4=x2*k;
        double y3=y1*k, y4=y2*k;
        double mx=(x3+x4)/2.0,my=(y3+y4)/2.0;
        double ans;
        double d = getDis(mx, my);
        if(d <= r){//判断反演点和半径的关系 如果两个反演点的中点到圆心的距离小于半径
            double dis = getDis(x3-x4,y3-y4);
            ans = dis*d0/r;
        }
        else{//其他的即是连线与圆相离时的状态 这时候的d点是p和q的反演点的连线的中垂线与圆的交点
            double kk=r/d;//其他的即是连线与圆相离时的状态 这时候的d点是p和q的反演点的连线的中垂线与圆的交点
            double smx = mx*kk, smy = my*kk;
            ans = 2*getDis(smx-x1,smy-y1);
        }
        printf("%.8f\n", ans);
    }
    return 0;
}

 

以上是关于2017多校第6场 HDU 6097 Mindis 计算几何,圆的反演的主要内容,如果未能解决你的问题,请参考以下文章

2017多校第6场 HDU 6096 String AC自动机

HDU 5358 多校第6场 First One

2018多校第6场 1013 hdu6373 Pinball

2017多校第10场 HDU 6172 Array Challenge 猜公式,矩阵幂

2017多校第10场 HDU 6181 Two Paths 次短路

2017多校第10场 HDU 6180 Schedule 贪心,multiset