• 计算几何模板--更新中


    weeping的私人模板,需要的话就拿去用吧(虽然可能会有小错,如果有请提醒我)

    这是现在日常用的模板

    2017.10.11 update:

      把白书上的半平面交模板换成别的了,原来的那个好像有问题,一直wa。

      把求三角形外心的模板改了精度更高的模板。

    2017.10.08 update:

      原来的模板太啰嗦,今天全部重新简化了,但同时保留以前模板。

     2017.09.04 update:

      1.发现射线法判断点在多边形内部的代码有误,已修正

    2017.08.06 update:

      1.卷包裹法求凸包 

      2.三角形和圆的相关模板

      3.O(logn)判断点在凸包内

    现在模板:

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cmath>
      4 #include <algorithm>
      5 
      6 
      7 using namespace std;
      8 const double PI = acos(-1.0);
      9 const double eps = 1e-10;
     10 
     11 /****************常用函数***************/
     12 //判断ta与tb的大小关系
     13 int sgn( double ta, double tb)
     14 {
     15     if(fabs(ta-tb)<eps)return 0;
     16     if(ta<tb)   return -1;
     17     return 1;
     18 }
     19 
     20 //
     21 class Point
     22 {
     23 public:
     24 
     25     double x, y;
     26 
     27     Point(){}
     28     Point( double tx, double ty){ x = tx, y = ty;}
     29 
     30     bool operator < (const Point &_se) const
     31     {
     32         return x<_se.x || (x==_se.x && y<_se.y);
     33     }
     34     friend Point operator + (const Point &_st,const Point &_se)
     35     {
     36         return Point(_st.x + _se.x, _st.y + _se.y);
     37     }
     38     friend Point operator - (const Point &_st,const Point &_se)
     39     {
     40         return Point(_st.x - _se.x, _st.y - _se.y);
     41     }
     42     //点位置相同(double类型)
     43     bool operator == (const Point &_off)const
     44     {
     45         return  sgn(x, _off.x) == 0 && sgn(y, _off.y) == 0;
     46     }
     47 
     48 };
     49 
     50 /****************常用函数***************/
     51 //点乘
     52 double dot(const Point &po,const Point &ps,const Point &pe)
     53 {
     54     return (ps.x - po.x) * (pe.x - po.x) + (ps.y - po.y) * (pe.y - po.y);
     55 }
     56 //叉乘
     57 double xmult(const Point &po,const Point &ps,const Point &pe)
     58 {
     59     return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y);
     60 }
     61 //两点间距离的平方
     62 double getdis2(const Point &st,const Point &se)
     63 {
     64     return (st.x - se.x) * (st.x - se.x) + (st.y - se.y) * (st.y - se.y);
     65 }
     66 //两点间距离
     67 double getdis(const Point &st,const Point &se)
     68 {
     69     return sqrt((st.x - se.x) * (st.x - se.x) + (st.y - se.y) * (st.y - se.y));
     70 }
     71 
     72 //两点表示的向量
     73 class Line
     74 {
     75 public:
     76 
     77     Point s, e;//两点表示,起点[s],终点[e]
     78     double a, b, c;//一般式,ax+by+c=0
     79     double angle;//向量的角度,[-pi,pi]
     80 
     81     Line(){}
     82     Line( Point ts, Point te):s(ts),e(te){}//get_angle();}
     83     Line(double _a,double _b,double _c):a(_a),b(_b),c(_c){}
     84 
     85     //排序用
     86     bool operator < (const Line &ta)const
     87     {
     88         return angle<ta.angle;
     89     }
     90     //向量与向量的叉乘
     91     friend double operator / ( const Line &_st, const  Line &_se)
     92     {
     93         return (_st.e.x - _st.s.x) * (_se.e.y - _se.s.y) - (_st.e.y - _st.s.y) * (_se.e.x - _se.s.x);
     94     }
     95     //向量间的点乘
     96     friend double operator *( const Line &_st, const  Line &_se)
     97     {
     98         return (_st.e.x - _st.s.x) * (_se.e.x - _se.s.x) - (_st.e.y - _st.s.y) * (_se.e.y - _se.s.y);
     99     }
    100     //从两点表示转换为一般表示
    101     //a=y2-y1,b=x1-x2,c=x2*y1-x1*y2
    102     bool pton()
    103     {
    104         a = e.y - s.y;
    105         b = s.x - e.x;
    106         c = e.x * s.y - e.y * s.x;
    107         return true;
    108     }
    109     //半平面交用
    110     //点在向量左边(右边的小于号改成大于号即可,在对应直线上则加上=号)
    111     friend bool operator < (const Point &_Off, const Line &_Ori)
    112     {
    113         return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x)
    114             < (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x);
    115     }
    116     //求直线或向量的角度
    117     double get_angle( bool isVector = true)
    118     {
    119         angle = atan2( e.y - s.y, e.x - s.x);
    120         if(!isVector && angle < 0)
    121             angle += PI;
    122         return angle;
    123     }
    124 
    125     //点在线段或直线上 1:点在直线上 2点在s,e所在矩形内
    126     bool has(const Point &_Off, bool isSegment = false) const
    127     {
    128         bool ff = sgn( xmult( s, e, _Off), 0) == 0;
    129         if( !isSegment) return ff;
    130         return ff
    131             && sgn(_Off.x - min(s.x, e.x), 0) >= 0 && sgn(_Off.x - max(s.x, e.x), 0) <= 0
    132             && sgn(_Off.y - min(s.y, e.y), 0) >= 0 && sgn(_Off.y - max(s.y, e.y), 0) <= 0;
    133     }
    134 
    135     //点到直线/线段的距离
    136     double dis(const Point &_Off, bool isSegment = false)
    137     {
    138         ///化为一般式
    139         pton();
    140         //到直线垂足的距离
    141         double td = (a * _Off.x + b * _Off.y + c) / sqrt(a * a + b * b);
    142         //如果是线段判断垂足
    143         if(isSegment)
    144         {
    145             double xp = (b * b * _Off.x - a * b * _Off.y - a * c) / ( a * a + b * b);
    146             double yp = (-a * b * _Off.x + a * a * _Off.y - b * c) / (a * a + b * b);
    147             double xb = max(s.x, e.x);
    148             double yb = max(s.y, e.y);
    149             double xs = s.x + e.x - xb;
    150             double ys = s.y + e.y - yb;
    151            if(xp > xb + eps || xp < xs - eps || yp > yb + eps || yp < ys - eps)
    152                 td = min( getdis(_Off,s), getdis(_Off,e));
    153         }
    154         return fabs(td);
    155     }
    156 
    157     //关于直线对称的点
    158     Point mirror(const Point &_Off)
    159     {
    160         ///注意先转为一般式
    161         Point ret;
    162         double d = a * a + b * b;
    163         ret.x = (b * b * _Off.x - a * a * _Off.x - 2 * a * b * _Off.y - 2 * a * c) / d;
    164         ret.y = (a * a * _Off.y - b * b * _Off.y - 2 * a * b * _Off.x - 2 * b * c) / d;
    165         return ret;
    166     }
    167     //计算两点的中垂线
    168     static Line ppline(const Point &_a,const Point &_b)
    169     {
    170         Line ret;
    171         ret.s.x = (_a.x + _b.x) / 2;
    172         ret.s.y = (_a.y + _b.y) / 2;
    173         //一般式
    174         ret.a = _b.x - _a.x;
    175         ret.b = _b.y - _a.y;
    176         ret.c = (_a.y - _b.y) * ret.s.y + (_a.x - _b.x) * ret.s.x;
    177         //两点式
    178         if(fabs(ret.a) > eps)
    179         {
    180             ret.e.y = 0.0;
    181             ret.e.x = - ret.c / ret.a;
    182             if(ret.e == ret. s)
    183             {
    184                 ret.e.y = 1e10;
    185                 ret.e.x = - (ret.c - ret.b * ret.e.y) / ret.a;
    186             }
    187         }
    188         else
    189         {
    190             ret.e.x = 0.0;
    191             ret.e.y = - ret.c / ret.b;
    192             if(ret.e == ret. s)
    193             {
    194                 ret.e.x = 1e10;
    195                 ret.e.y = - (ret.c - ret.a * ret.e.x) / ret.b;
    196             }
    197         }
    198         return ret;
    199     }
    200 
    201     //------------直线和直线(向量)-------------
    202     //向量向左边平移t的距离
    203     Line& moveLine( double t)
    204     {
    205         Point of;
    206         of = Point( -( e.y - s.y), e.x - s.x);
    207         double dis = sqrt( of.x * of.x + of.y * of.y);
    208         of.x= of.x * t / dis, of.y = of.y * t / dis;
    209         s = s + of, e = e + of;
    210         return *this;
    211     }
    212     //直线重合
    213     static bool equal(const Line &_st,const Line &_se)
    214     {
    215         return _st.has( _se.e) && _se.has( _st.s);
    216     }
    217     //直线平行
    218     static bool parallel(const Line &_st,const Line &_se)
    219     {
    220         return sgn( _st / _se, 0) == 0;
    221     }
    222     //两直线(线段)交点
    223     //返回-1代表平行,0代表重合,1代表相交
    224     static bool crossLPt(const Line &_st,const Line &_se, Point &ret)
    225     {
    226         if(parallel(_st,_se))
    227         {
    228             if(Line::equal(_st,_se)) return 0;
    229             return -1;
    230         }
    231         ret = _st.s;
    232         double t = ( Line(_st.s,_se.s) / _se) / ( _st / _se);
    233         ret.x += (_st.e.x - _st.s.x) * t;
    234         ret.y += (_st.e.y - _st.s.y) * t;
    235         return 1;
    236     }
    237     //------------线段和直线(向量)----------
    238     //直线和线段相交
    239     //参数:直线[_st],线段[_se]
    240     friend bool crossSL( Line &_st, Line &_se)
    241     {
    242         return sgn( xmult( _st.s, _se.s, _st.e) * xmult( _st.s, _st.e, _se.e), 0) >= 0;
    243     }
    244 
    245     //判断线段是否相交(注意添加eps)
    246     static bool isCrossSS( const Line &_st, const  Line &_se)
    247     {
    248         //1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交
    249         //2.跨立试验(等于0时端点重合)
    250         return
    251             max(_st.s.x, _st.e.x) >= min(_se.s.x, _se.e.x) &&
    252             max(_se.s.x, _se.e.x) >= min(_st.s.x, _st.e.x) &&
    253             max(_st.s.y, _st.e.y) >= min(_se.s.y, _se.e.y) &&
    254             max(_se.s.y, _se.e.y) >= min(_st.s.y, _st.e.y) &&
    255             sgn( xmult( _se.s, _st.s, _se.e) * xmult( _se.s, _se.e, _st.s), 0) >= 0 &&
    256             sgn( xmult( _st.s, _se.s, _st.e) * xmult( _st.s, _st.e, _se.s), 0) >= 0;
    257     }
    258 };
    259 
    260 //寻找凸包的graham 扫描法所需的排序函数
    261 Point gsort;
    262 bool gcmp( const Point &ta, const Point &tb)/// 选取与最后一条确定边夹角最小的点,即余弦值最大者
    263 {
    264     double tmp = xmult( gsort, ta, tb);
    265     if( fabs( tmp) < eps)
    266         return getdis( gsort, ta) < getdis( gsort, tb);
    267     else if( tmp > 0)
    268         return 1;
    269     return 0;
    270 }
    271 
    272 class Polygon
    273 {
    274 public:
    275     const static int maxpn = 5e4+7;
    276     Point pt[maxpn];//点(顺时针或逆时针)
    277     Line dq[maxpn]; //求半平面交打开注释
    278     int n;//点的个数
    279 
    280 
    281     //求多边形面积,多边形内点必须顺时针或逆时针
    282     double area()
    283     {
    284         double ans = 0.0;
    285         for(int i = 0; i < n; i ++)
    286         {
    287             int nt = (i + 1) % n;
    288             ans += pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    289         }
    290         return fabs( ans / 2.0);
    291     }
    292     //求多边形重心,多边形内点必须顺时针或逆时针
    293     Point gravity()
    294     {
    295         Point ans;
    296         ans.x = ans.y = 0.0;
    297         double area = 0.0;
    298         for(int i = 0; i < n; i ++)
    299         {
    300             int nt = (i + 1) % n;
    301             double tp = pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    302             area += tp;
    303             ans.x += tp * (pt[i].x + pt[nt].x);
    304             ans.y += tp * (pt[i].y + pt[nt].y);
    305         }
    306         ans.x /= 3 * area;
    307         ans.y /= 3 * area;
    308         return ans;
    309     }
    310     //判断点是否在任意多边形内[射线法],O(n)
    311     bool ahas( Point &_Off)
    312     {
    313         int ret = 0;
    314         double infv = 1e20;//坐标系最大范围
    315         Line l = Line( _Off, Point( -infv ,_Off.y));
    316         for(int i = 0; i < n; i ++)
    317         {
    318             Line ln = Line( pt[i], pt[(i + 1) % n]);
    319             if(fabs(ln.s.y - ln.e.y) > eps)
    320             {
    321                 Point tp = (ln.s.y > ln.e.y)? ln.s: ln.e;
    322                 if( ( fabs( tp.y - _Off.y) < eps && tp.x < _Off.x + eps) || Line::isCrossSS( ln, l))
    323                     ret++;
    324             }
    325             else if( Line::isCrossSS( ln, l))
    326                 ret++;
    327         }
    328         return ret&1;
    329     }
    330 
    331     //判断任意点是否在凸包内,O(logn)
    332     bool bhas( Point & p)
    333     {
    334         if( n < 3)
    335             return false;
    336         if( xmult( pt[0], p, pt[1]) > eps)
    337             return false;
    338         if( xmult( pt[0], p, pt[n-1]) < -eps)
    339             return false;
    340         int l = 2,r = n-1;
    341         int line = -1;
    342         while( l <= r)
    343         {
    344             int mid = ( l + r) >> 1;
    345             if( xmult( pt[0], p, pt[mid]) >= 0)
    346                 line = mid,r = mid - 1;
    347             else l = mid + 1;
    348         }
    349         return xmult( pt[line-1], p, pt[line]) <= eps;
    350     }
    351 
    352 
    353 
    354     //凸多边形被直线分割
    355     Polygon split( Line &_Off)
    356     {
    357         //注意确保多边形能被分割
    358         Polygon ret;
    359         Point spt[2];
    360         double tp = 0.0, np;
    361         bool flag = true;
    362         int i, pn = 0, spn = 0;
    363         for(i = 0; i < n; i ++)
    364         {
    365             if(flag)
    366                 pt[pn ++] = pt[i];
    367             else
    368                 ret.pt[ret.n ++] = pt[i];
    369             np = xmult( _Off.s, _Off.e, pt[(i + 1) % n]);
    370             if(tp * np < -eps)
    371             {
    372                 flag = !flag;
    373                 Line::crossLPt( _Off, Line(pt[i], pt[(i + 1) % n]), spt[spn++]);
    374             }
    375             tp = (fabs(np) > eps)?np: tp;
    376         }
    377         ret.pt[ret.n ++] = spt[0];
    378         ret.pt[ret.n ++] = spt[1];
    379         n = pn;
    380         return ret;
    381     }
    382 
    383 
    384     /** 卷包裹法求点集凸包,_p为输入点集,_n为点的数量 **/
    385     void ConvexClosure( Point _p[], int _n)
    386     {
    387         sort( _p, _p + _n);
    388         n = 0;
    389         for(int i = 0; i < _n; i++)
    390         {
    391             while( n > 1 && sgn( xmult( pt[n-2], pt[n-1], _p[i]), 0) <= 0)
    392                 n--;
    393             pt[n++] = _p[i];
    394         }
    395         int _key = n;
    396         for(int i = _n - 2; i >= 0; i--)
    397         {
    398             while( n > _key && sgn( xmult( pt[n-2], pt[n-1], _p[i]), 0) <= 0)
    399                 n--;
    400             pt[n++] = _p[i];
    401         }
    402         if(n>1)   n--;//除去重复的点,该点已是凸包凸包起点
    403     }
    404     /****** 寻找凸包的graham 扫描法********************/
    405     /****** _p为输入的点集,_n为点的数量****************/
    406 
    407     void graham( Point _p[], int _n)
    408     {
    409         int cur=0;
    410         for(int i = 1; i < _n; i++)
    411             if( sgn( _p[cur].y, _p[i].y) > 0 || ( sgn( _p[cur].y, _p[i].y) == 0 && sgn( _p[cur].x, _p[i].x) > 0) )
    412                 cur = i;
    413         swap( _p[cur], _p[0]);
    414         n = 0, gsort = pt[n++] = _p[0];
    415         if( _n <= 1)   return;
    416         sort( _p + 1, _p+_n ,gcmp);
    417         pt[n++] = _p[1];
    418         for(int i = 2; i < _n; i++)
    419         {
    420             while(n>1 && sgn( xmult( pt[n-2], pt[n-1], _p[i]), 0) <= 0)// 当凸包退化成直线时需特别注意n
    421                 n--;
    422             pt[n++] = _p[i];
    423         }
    424     }
    425     //凸包旋转卡壳(注意点必须顺时针或逆时针排列)
    426     //返回值凸包直径的平方(最远两点距离的平方)
    427     pair<Point,Point> rotating_calipers()
    428     {
    429         int i = 1 % n;
    430         double ret = 0.0;
    431         pt[n] = pt[0];
    432         pair<Point,Point>ans=make_pair(pt[0],pt[0]);
    433         for(int j = 0; j < n; j ++)
    434         {
    435             while( fabs( xmult( pt[i+1], pt[j], pt[j + 1])) > fabs( xmult( pt[i], pt[j], pt[j + 1])) + eps)
    436                 i = (i + 1) % n;
    437             //pt[i]和pt[j],pt[i + 1]和pt[j + 1]可能是对踵点
    438             if(ret < getdis2(pt[i],pt[j]))  ret = getdis2(pt[i],pt[j]), ans = make_pair(pt[i],pt[j]);
    439             if(ret < getdis2(pt[i+1],pt[j+1]))  ret = getdis(pt[i+1],pt[j+1]), ans = make_pair(pt[i+1],pt[j+1]);
    440         }
    441         return ans;
    442     }
    443 
    444     //凸包旋转卡壳(注意点必须逆时针排列)
    445     //返回值两凸包的最短距离
    446     double rotating_calipers( Polygon &_Off)
    447     {
    448         int i = 0;
    449         double ret = 1e10;//inf
    450         pt[n] = pt[0];
    451         _Off.pt[_Off.n] = _Off.pt[0];
    452         //注意凸包必须逆时针排列且pt[0]是左下角点的位置
    453         while( _Off.pt[i + 1].y > _Off.pt[i].y)
    454             i = (i + 1) % _Off.n;
    455         for(int j = 0; j < n; j ++)
    456         {
    457             double tp;
    458             //逆时针时为 >,顺时针则相反
    459             while((tp = xmult(_Off.pt[i + 1],pt[j], pt[j + 1]) - xmult(_Off.pt[i], pt[j], pt[j + 1])) > eps)
    460                 i = (i + 1) % _Off.n;
    461             //(pt[i],pt[i+1])和(_Off.pt[j],_Off.pt[j + 1])可能是最近线段
    462             ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i], true));
    463             ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j + 1], true));
    464             if(tp > -eps)//如果不考虑TLE问题最好不要加这个判断
    465             {
    466                 ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i + 1], true));
    467                 ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j], true));
    468             }
    469         }
    470         return ret;
    471     }
    472 
    473     //-----------半平面交-------------
    474     //复杂度:O(nlog2(n))
    475     //获取半平面交的多边形(多边形的核)
    476     //参数:向量集合[l],向量数量[ln];(半平面方向在向量左边)
    477     //函数运行后如果n[即返回多边形的点数量]为0则不存在半平面交的多边形(不存在区域或区域面积无穷大)
    478     int judege( Line &_lx, Line &_ly, Line &_lz)
    479     {
    480         Point tmp;
    481         Line::crossLPt(_lx,_ly,tmp);
    482         return sgn(xmult(_lz.s,tmp,_lz.e),0);
    483     }
    484     int halfPanelCross(Line L[], int ln)
    485     {
    486         int i, tn, bot, top;
    487         for(int i = 0; i < ln; i++)
    488             L[i].get_angle();
    489         sort(L, L + ln);
    490         //平面在向量左边的筛选
    491         for(i = tn = 1; i < ln; i ++)
    492             if(fabs(L[i].angle - L[i - 1].angle) > eps)
    493                 L[tn ++] = L[i];
    494         ln = tn, n = 0, bot = 0, top = 1;
    495         dq[0] = L[0], dq[1] = L[1];
    496         for(i = 2; i < ln; i ++)
    497         {
    498             while(bot < top &&  judege(dq[top],dq[top-1],L[i]) > 0)
    499                 top --;
    500             while(bot < top &&  judege(dq[bot],dq[bot+1],L[i]) > 0)
    501                 bot ++;
    502             dq[++ top] = L[i];
    503         }
    504         while(bot < top && judege(dq[top],dq[top-1],dq[bot]) > 0)
    505             top --;
    506         while(bot < top && judege(dq[bot],dq[bot+1],dq[top]) > 0)
    507             bot ++;
    508         //若半平面交退化为点或线
    509         //        if(top <= bot + 1)
    510         //            return 0;
    511         dq[++top] = dq[bot];
    512         for(i = bot; i < top; i ++)
    513             Line::crossLPt(dq[i],dq[i + 1],pt[n++]);
    514         return n;
    515     }
    516 };
    517 
    518 
    519 class Circle
    520 {
    521 public:
    522     Point c;//圆心
    523     double r;//半径
    524     double db, de;//圆弧度数起点, 圆弧度数终点(逆时针0-360)
    525 
    526     //-------圆---------
    527 
    528     //判断圆在多边形内
    529     bool inside( Polygon &_Off)
    530     {
    531         if(_Off.ahas(c) == false)
    532             return false;
    533         for(int i = 0; i < _Off.n; i ++)
    534         {
    535             Line l = Line(_Off.pt[i], _Off.pt[(i + 1) % _Off.n]);
    536             if(l.dis(c, true) < r - eps)
    537                 return false;
    538         }
    539         return true;
    540     }
    541 
    542     //判断多边形在圆内(线段和折线类似)
    543     bool has( Polygon &_Off)
    544     {
    545         for(int i = 0; i < _Off.n; i ++)
    546             if( getdis2(_Off.pt[i],c) > r * r - eps)
    547                 return false;
    548         return true;
    549     }
    550 
    551     //-------圆弧-------
    552     //圆被其他圆截得的圆弧,参数:圆[_Off]
    553     Circle operator-(Circle &_Off) const
    554     {
    555         //注意圆必须相交,圆心不能重合
    556         double d2 = getdis2(c,_Off.c);
    557         double d = getdis(c,_Off.c);
    558         double ans = acos((d2 + r * r - _Off.r * _Off.r) / (2 * d * r));
    559         Point py = _Off.c - c;
    560         double oans = atan2(py.y, py.x);
    561         Circle res;
    562         res.c = c;
    563         res.r = r;
    564         res.db = oans + ans;
    565         res.de = oans - ans + 2 * PI;
    566         return res;
    567     }
    568     //圆被其他圆截得的圆弧,参数:圆[_Off]
    569     Circle operator+(Circle &_Off) const
    570     {
    571         //注意圆必须相交,圆心不能重合
    572         double d2 = getdis2(c,_Off.c);
    573         double d = getdis(c,_Off.c);
    574         double ans = acos((d2 + r * r - _Off.r * _Off.r) / (2 * d * r));
    575         Point py = _Off.c - c;
    576         double oans = atan2(py.y, py.x);
    577         Circle res;
    578         res.c = c;
    579         res.r = r;
    580         res.db = oans - ans;
    581         res.de = oans + ans;
    582         return res;
    583     }
    584 
    585     //过圆外一点的两条切线
    586     //参数:点[_Off](必须在圆外),返回:两条切线(切线的s点为_Off,e点为切点)
    587     pair<Line, Line>  tangent( Point &_Off)
    588     {
    589         double d = getdis(c,_Off);
    590         //计算角度偏移的方式
    591         double angp = acos(r / d), ango = atan2(_Off.y - c.y, _Off.x - c.x);
    592         Point pl = Point(c.x + r * cos(ango + angp), c.y + r * sin(ango + angp)),
    593             pr = Point(c.x + r * cos(ango - angp), c.y + r * sin(ango - angp));
    594         return make_pair(Line(_Off, pl), Line(_Off, pr));
    595     }
    596 
    597     //计算直线和圆的两个交点
    598     //参数:直线[_Off](两点式),返回两个交点,注意直线必须和圆有两个交点
    599     pair<Point, Point> cross(Line _Off)
    600     {
    601         _Off.pton();
    602         //到直线垂足的距离
    603         double td = fabs(_Off.a * c.x + _Off.b * c.y + _Off.c) / sqrt(_Off.a * _Off.a + _Off.b * _Off.b);
    604 
    605         //计算垂足坐标
    606         double xp = (_Off.b * _Off.b * c.x - _Off.a * _Off.b * c.y - _Off.a * _Off.c) / ( _Off.a * _Off.a + _Off.b * _Off.b);
    607         double yp = (- _Off.a * _Off.b * c.x + _Off.a * _Off.a * c.y - _Off.b * _Off.c) / (_Off.a * _Off.a + _Off.b * _Off.b);
    608 
    609         double ango = atan2(yp - c.y, xp - c.x);
    610         double angp = acos(td / r);
    611 
    612         return make_pair(Point(c.x + r * cos(ango + angp), c.y + r * sin(ango + angp)),
    613             Point(c.x + r * cos(ango - angp), c.y + r * sin(ango - angp)));
    614     }
    615 };
    616 
    617 class triangle
    618 {
    619 public:
    620     Point a, b, c;//顶点
    621     triangle(){}
    622     triangle(Point a, Point b, Point c): a(a), b(b), c(c){}
    623 
    624     //计算三角形面积
    625     double area()
    626     {
    627         return fabs( xmult(a, b, c)) / 2.0;
    628     }
    629 
    630     //计算三角形外心
    631     //返回:外接圆圆心
    632     Point circumcenter()
    633     {
    634         double pa = a.x * a.x + a.y * a.y;
    635         double pb = b.x * b.x + b.y * b.y;
    636         double pc = c.x * c.x + c.y * c.y;
    637         double ta = pa * ( b.y - c.y) - pb * ( a.y - c.y) + pc * ( a.y - b.y);
    638         double tb = -pa * ( b.x - c.x) + pb * ( a.x - c.x) - pc * ( a.x - b.x);
    639         double tc = a.x * ( b.y - c.y) - b.x * ( a.y - c.y) + c.x * ( a.y - b.y);
    640         return Point( ta / 2.0 / tc, tb / 2.0 / tc);
    641     }
    642 
    643     //计算三角形内心
    644     //返回:内接圆圆心
    645     Point incenter()
    646     {
    647         Line u, v;
    648         double m, n;
    649         u.s = a;
    650         m = atan2(b.y - a.y, b.x - a.x);
    651         n = atan2(c.y - a.y, c.x - a.x);
    652         u.e.x = u.s.x + cos((m + n) / 2);
    653         u.e.y = u.s.y + sin((m + n) / 2);
    654         v.s = b;
    655         m = atan2(a.y - b.y, a.x - b.x);
    656         n = atan2(c.y - b.y, c.x - b.x);
    657         v.e.x = v.s.x + cos((m + n) / 2);
    658         v.e.y = v.s.y + sin((m + n) / 2);
    659         Point ret;
    660         Line::crossLPt(u,v,ret);
    661         return ret;
    662     }
    663 
    664     //计算三角形垂心
    665     //返回:高的交点
    666     Point perpencenter()
    667     {
    668         Line u,v;
    669         u.s = c;
    670         u.e.x = u.s.x - a.y + b.y;
    671         u.e.y = u.s.y + a.x - b.x;
    672         v.s = b;
    673         v.e.x = v.s.x - a.y + c.y;
    674         v.e.y = v.s.y + a.x - c.x;
    675         Point ret;
    676         Line::crossLPt(u,v,ret);
    677         return ret;
    678     }
    679 
    680     //计算三角形重心
    681     //返回:重心
    682     //到三角形三顶点距离的平方和最小的点
    683     //三角形内到三边距离之积最大的点
    684     Point barycenter()
    685     {
    686         Line u,v;
    687         u.s.x = (a.x + b.x) / 2;
    688         u.s.y = (a.y + b.y) / 2;
    689         u.e = c;
    690         v.s.x = (a.x + c.x) / 2;
    691         v.s.y = (a.y + c.y) / 2;
    692         v.e = b;
    693         Point ret;
    694         Line::crossLPt(u,v,ret);
    695         return ret;
    696     }
    697 
    698     //计算三角形费马点
    699     //返回:到三角形三顶点距离之和最小的点
    700     Point fermentPoint()
    701     {
    702         Point u, v;
    703         double step = fabs(a.x) + fabs(a.y) + fabs(b.x) + fabs(b.y) + fabs(c.x) + fabs(c.y);
    704         int i, j, k;
    705         u.x = (a.x + b.x + c.x) / 3;
    706         u.y = (a.y + b.y + c.y) / 3;
    707         while (step > eps)
    708         {
    709             for (k = 0; k < 10; step /= 2, k ++)
    710             {
    711                 for (i = -1; i <= 1; i ++)
    712                 {
    713                     for (j =- 1; j <= 1; j ++)
    714                     {
    715                         v.x = u.x + step * i;
    716                         v.y = u.y + step * j;
    717                         if (getdis(u,a) + getdis(u,b) + getdis(u,c) > getdis(v,a) + getdis(v,b) + getdis(v,c))
    718                             u = v;
    719                     }
    720                 }
    721             }
    722         }
    723         return u;
    724     }
    725 };
    726 
    727 int main(void)
    728 {
    729 
    730     return 0;
    731 }

    以前模板:

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cmath>
      4 #include <algorithm>
      5 
      6 
      7 using namespace std;
      8 const double PI = acos(-1.0);
      9 const double eps = 1e-10;
     10 //
     11 class Point
     12 {
     13 public:
     14     double x, y;
     15 
     16     Point(){}
     17     Point(double x, double y):x(x),y(y){}
     18 
     19     bool operator < (const Point &_se) const
     20     {
     21         return x<_se.x || (x==_se.x && y<_se.y);
     22     }
     23     /*******判断ta与tb的大小关系*******/
     24     static int sgn(double ta,double tb)
     25     {
     26         if(fabs(ta-tb)<eps)return 0;
     27         if(ta<tb)   return -1;
     28         return 1;
     29     }
     30     static double xmult(const Point &po, const Point &ps, const Point &pe)
     31     {
     32         return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y);
     33     }
     34     friend Point operator + (const Point &_st,const Point &_se)
     35     {
     36         return Point(_st.x + _se.x, _st.y + _se.y);
     37     }
     38     friend Point operator - (const Point &_st,const Point &_se)
     39     {
     40         return Point(_st.x - _se.x, _st.y - _se.y);
     41     }
     42     //点位置相同(double类型)
     43     bool operator == (const Point &_off) const
     44     {
     45         return  Point::sgn(x, _off.x) == 0 && Point::sgn(y, _off.y) == 0;
     46     }
     47     //点位置不同(double类型)
     48     bool operator != (const Point &_Off) const
     49     {
     50         return ((*this) == _Off) == false;
     51     }
     52     //两点间距离的平方
     53     static double dis2(const Point &_st,const Point &_se)
     54     {
     55         return (_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y);
     56     }
     57     //两点间距离
     58     static double dis(const Point &_st, const Point &_se)
     59     {
     60         return sqrt((_st.x - _se.x) * (_st.x - _se.x) + (_st.y - _se.y) * (_st.y - _se.y));
     61     }
     62 };
     63 //两点表示的向量
     64 class Line
     65 {
     66 public:
     67     Point s, e;//两点表示,起点[s],终点[e]
     68     double a, b, c;//一般式,ax+by+c=0
     69     double angle;//向量的角度,[-pi,pi]
     70     Line(){}
     71     Line(const Point &s, const Point &e):s(s),e(e){get_angle(1);}
     72     Line(double _a,double _b,double _c):a(_a),b(_b),c(_c){}
     73 
     74     //向量与点的叉乘,参数:点[_Off]
     75     //[点相对向量位置判断]
     76     double operator /(const Point &_Off) const
     77     {
     78         return (_Off.y - s.y) * (e.x - s.x) - (_Off.x - s.x) * (e.y - s.y);
     79     }
     80     //向量与向量的叉乘,参数:向量[_Off]
     81     friend double operator /(const Line &_st,const Line &_se)
     82     {
     83         return (_st.e.x - _st.s.x) * (_se.e.y - _se.s.y) - (_st.e.y - _st.s.y) * (_se.e.x - _se.s.x);
     84     }
     85     friend double operator *(const Line &_st,const Line &_se)
     86     {
     87         return (_st.e.x - _st.s.x) * (_se.e.x - _se.s.x) - (_st.e.y - _st.s.y) * (_se.e.y - _se.s.y);
     88     }
     89     //从两点表示转换为一般表示
     90     //a=y2-y1,b=x1-x2,c=x2*y1-x1*y2
     91     bool pton()
     92     {
     93         a = e.y - s.y;
     94         b = s.x - e.x;
     95         c = e.x * s.y - e.y * s.x;
     96         return true;
     97     }
     98     //求直线或向量的角度
     99     double get_angle(bool isVector)
    100     {
    101         angle=atan2(e.y-s.y,e.x-s.x);
    102         if(!isVector && angle<0)
    103             angle+=PI;
    104         return angle;
    105     }
    106 
    107     //
    108     bool operator < (const Line &ta)const
    109     {
    110         return angle<ta.angle;
    111     }
    112     //-----------点和直线(向量)-----------
    113     //点在向量左边(右边的小于号改成大于号即可,在对应直线上则加上=号)
    114     //参数:点[_Off],向量[_Ori]
    115     friend bool operator<(const Point &_Off, const Line &_Ori)
    116     {
    117         return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x)
    118             < (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x);
    119     }
    120 
    121     //点在直线上,参数:点[_Off]
    122     bool lhas(const Point &_Off) const
    123     {
    124         return Point::sgn((*this) / _Off, 0) == 0;
    125     }
    126     //点在线段上,参数:点[_Off]
    127     bool shas(const Point &_Off) const
    128     {
    129         return lhas(_Off)
    130             && Point::sgn(_Off.x - min(s.x, e.x), 0) > 0 && Point::sgn(_Off.x - max(s.x, e.x), 0) < 0
    131             && Point::sgn(_Off.y - min(s.y, e.y), 0) > 0 && Point::sgn(_Off.y - max(s.y, e.y), 0) < 0;
    132     }
    133 
    134     //点到直线/线段的距离
    135     //参数: 点[_Off], 是否是线段[isSegment](默认为直线)
    136     double dis(const Point &_Off, bool isSegment = false)
    137     {
    138         ///化为一般式
    139         pton();
    140 
    141         //到直线垂足的距离
    142         double td = (a * _Off.x + b * _Off.y + c) / sqrt(a * a + b * b);
    143 
    144         //如果是线段判断垂足
    145         if(isSegment)
    146         {
    147             double xp = (b * b * _Off.x - a * b * _Off.y - a * c) / ( a * a + b * b);
    148             double yp = (-a * b * _Off.x + a * a * _Off.y - b * c) / (a * a + b * b);
    149             double xb = max(s.x, e.x);
    150             double yb = max(s.y, e.y);
    151             double xs = s.x + e.x - xb;
    152             double ys = s.y + e.y - yb;
    153             if(xp > xb + eps || xp < xs - eps || yp > yb + eps || yp < ys - eps)
    154                 td = min(Point::dis(_Off,s), Point::dis(_Off,e));
    155         }
    156 
    157         return fabs(td);
    158     }
    159 
    160     //关于直线对称的点
    161     Point mirror(const Point &_Off) const
    162     {
    163         ///注意先转为一般式
    164         Point ret;
    165         double d = a * a + b * b;
    166         ret.x = (b * b * _Off.x - a * a * _Off.x - 2 * a * b * _Off.y - 2 * a * c) / d;
    167         ret.y = (a * a * _Off.y - b * b * _Off.y - 2 * a * b * _Off.x - 2 * b * c) / d;
    168         return ret;
    169     }
    170     //计算两点的中垂线
    171     static Line ppline(const Point &_a, const Point &_b)
    172     {
    173         Line ret;
    174         ret.s.x = (_a.x + _b.x) / 2;
    175         ret.s.y = (_a.y + _b.y) / 2;
    176         //一般式
    177         ret.a = _b.x - _a.x;
    178         ret.b = _b.y - _a.y;
    179         ret.c = (_a.y - _b.y) * ret.s.y + (_a.x - _b.x) * ret.s.x;
    180         //两点式
    181         if(std::fabs(ret.a) > eps)
    182         {
    183             ret.e.y = 0.0;
    184             ret.e.x = - ret.c / ret.a;
    185             if(ret.e == ret. s)
    186             {
    187                 ret.e.y = 1e10;
    188                 ret.e.x = - (ret.c - ret.b * ret.e.y) / ret.a;
    189             }
    190         }
    191         else
    192         {
    193             ret.e.x = 0.0;
    194             ret.e.y = - ret.c / ret.b;
    195             if(ret.e == ret. s)
    196             {
    197                 ret.e.x = 1e10;
    198                 ret.e.y = - (ret.c - ret.a * ret.e.x) / ret.b;
    199             }
    200         }
    201         return ret;
    202     }
    203 
    204     //------------直线和直线(向量)-------------
    205     //直线向左边平移t的距离
    206     Line& moveLine(double t)
    207     {
    208         Point of;
    209         of=Point(-(e.y-s.y),e.x-s.x);
    210         double dis=sqrt(of.x*of.x+of.y*of.y);
    211         of.x=of.x*t/dis,of.y=of.y*t/dis;
    212         s=s+of,e=e+of;
    213         return *this;
    214     }
    215     //直线重合,参数:直线向量[_st],[_se]
    216     static bool equal(const Line &_st, const Line &_se)
    217     {
    218         return _st.lhas(_se.e) && _se.lhas(_se.s);
    219     }
    220     //直线平行,参数:直线向量[_st],[_se]
    221     static bool parallel(const Line &_st,const Line &_se)
    222     {
    223         return Point::sgn(_st / _se, 0) == 0;
    224     }
    225     //两直线(线段)交点,参数:直线向量[_st],[_se],交点
    226     //返回-1代表平行,0代表重合,1代表相交
    227     static bool crossLPt(const Line &_st,const Line &_se,Point &ret)
    228     {
    229         if(Line::parallel(_st,_se))
    230         {
    231             if(Line::equal(_st,_se)) return 0;
    232             return -1;
    233         }
    234         ret = _st.s;
    235         double t = (Line(_st.s,_se.s)/_se)/(_st/_se);
    236         ret.x += (_st.e.x - _st.s.x) * t;
    237         ret.y += (_st.e.y - _st.s.y) * t;
    238         return 1;
    239     }
    240     //------------线段和直线(向量)----------
    241     //线段和直线交
    242     //参数:直线[_st],线段[_se]
    243     friend bool crossSL(const Line &_st,const Line &_se)
    244     {
    245         return Point::sgn((_st / _se.s) * (_st / _se.e) ,0) <= 0;
    246     }
    247 
    248     //------------线段和线段(向量)----------
    249     //判断线段是否相交(注意添加eps),参数:线段[_st],线段[_se]
    250     static bool isCrossSS(const Line &_st,const Line &_se)
    251     {
    252         //1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交
    253         //2.跨立试验(等于0时端点重合)
    254         return
    255             max(_st.s.x, _st.e.x) >= min(_se.s.x, _se.e.x) &&
    256             max(_se.s.x, _se.e.x) >= min(_st.s.x, _st.e.x) &&
    257             max(_st.s.y, _st.e.y) >= min(_se.s.y, _se.e.y) &&
    258             max(_se.s.y, _se.e.y) >= min(_st.s.y, _st.e.y) &&
    259             Point::sgn((_st / Line(_st.s, _se.s)) * (_st / Line(_st.s, _se.e)), 0) <= 0 &&
    260             Point::sgn((_se / Line(_se.s, _st.s)) * (_se / Line(_se.s, _st.e)), 0) <= 0;
    261     }
    262 };
    263 
    264 class Polygon
    265 {
    266 public:
    267     const static int maxpn = 5e4+7;
    268     Point pt[maxpn];//点(顺时针或逆时针)
    269     int n;//点的个数
    270 
    271 
    272     //求多边形面积,多边形内点必须顺时针或逆时针
    273     double area() const
    274     {
    275         double ans = 0.0;
    276         for(int i = 0; i < n; i ++)
    277         {
    278             int nt = (i + 1) % n;
    279             ans += pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    280         }
    281         return fabs(ans / 2.0);
    282     }
    283     //求多边形重心,多边形内点必须顺时针或逆时针
    284     Point gravity() const
    285     {
    286         Point ans;
    287         ans.x = ans.y = 0.0;
    288         double area = 0.0;
    289         for(int i = 0; i < n; i ++)
    290         {
    291             int nt = (i + 1) % n;
    292             double tp = pt[i].x * pt[nt].y - pt[nt].x * pt[i].y;
    293             area += tp;
    294             ans.x += tp * (pt[i].x + pt[nt].x);
    295             ans.y += tp * (pt[i].y + pt[nt].y);
    296         }
    297         ans.x /= 3 * area;
    298         ans.y /= 3 * area;
    299         return ans;
    300     }
    301     //判断点在凸多边形内,参数:点[_Off]
    302     bool chas(const Point &_Off) const
    303     {
    304         double tp = 0, np;
    305         for(int i = 0; i < n; i ++)
    306         {
    307             np = Line(pt[i], pt[(i + 1) % n]) / _Off;
    308             if(tp * np < -eps)
    309                 return false;
    310             tp = (fabs(np) > eps)?np: tp;
    311         }
    312         return true;
    313     }
    314     //判断点是否在任意多边形内[射线法],O(n)
    315     bool ahas(const Point &_Off) const
    316     {
    317         int ret = 0;
    318         double infv = 1e20;//坐标系最大范围
    319         Line l = Line(_Off, Point( -infv ,_Off.y));
    320         for(int i = 0; i < n; i ++)
    321         {
    322             Line ln = Line(pt[i], pt[(i + 1) % n]);
    323             if(fabs(ln.s.y - ln.e.y) > eps)
    324             {
    325                 Point tp = (ln.s.y > ln.e.y)? ln.s: ln.e;
    326                 if((fabs(tp.y - _Off.y) < eps && tp.x < _Off.x + eps)||Line::isCrossSS(ln,l))
    327                     ret ++;
    328             }
    329             else if(Line::isCrossSS(ln,l))
    330                 ret ++;
    331         }
    332         return ret&1;
    333     }
    334 
    335     //判断任意点是否在凸包内,O(lgn)
    336     bool bhas(const Point & p)
    337     {
    338         if(n<3)
    339             return false;
    340         if(Point::xmult(pt[0],p,pt[1])>eps)
    341             return false;
    342         if(Point::xmult(pt[0],p,pt[n-1])<-eps)
    343             return false;
    344         int l=2,r=n-1;
    345         int line=-1;
    346         while(l<=r)
    347         {
    348             int mid=(l+r)>>1;
    349             if(Point::xmult(pt[0],p,pt[mid])>=0)
    350                 line=mid,r=mid-1;
    351             else l=mid+1;
    352         }
    353         return Point::xmult(pt[line-1],p,pt[line])<=eps;
    354     }
    355 
    356 
    357 
    358     //凸多边形被直线分割,参数:直线[_Off]
    359     Polygon split(Line _Off)
    360     {
    361         //注意确保多边形能被分割
    362         Polygon ret;
    363         Point spt[2];
    364         double tp = 0.0, np;
    365         bool flag = true;
    366         int i, pn = 0, spn = 0;
    367         for(i = 0; i < n; i ++)
    368         {
    369             if(flag)
    370                 pt[pn ++] = pt[i];
    371             else
    372                 ret.pt[ret.n ++] = pt[i];
    373             np = _Off / pt[(i + 1) % n];
    374             if(tp * np < -eps)
    375             {
    376                 flag = !flag;
    377                 Line::crossLPt(_Off,Line(pt[i], pt[(i + 1) % n]),spt[spn++]);
    378             }
    379             tp = (fabs(np) > eps)?np: tp;
    380         }
    381         ret.pt[ret.n ++] = spt[0];
    382         ret.pt[ret.n ++] = spt[1];
    383         n = pn;
    384         return ret;
    385     }
    386 
    387 
    388     /** 卷包裹法求点集凸包,_p为输入点集,_n为点的数量 **/
    389     void ConvexClosure(Point _p[],int _n)
    390     {
    391         sort(_p,_p+_n);
    392         n=0;
    393         for(int i=0;i<_n;i++)
    394         {
    395             while(n>1&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<=0)
    396                 n--;
    397             pt[n++]=_p[i];
    398         }
    399         int _key=n;
    400         for(int i=_n-2;i>=0;i--)
    401         {
    402             while(n>_key&&Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<=0)
    403                 n--;
    404             pt[n++]=_p[i];
    405         }
    406         if(n>1)   n--;//除去重复的点,该点已是凸包凸包起点
    407     }
    408 //    /****** 寻找凸包的graham 扫描法********************/
    409 //    /****** _p为输入的点集,_n为点的数量****************/
    410 //    /**使用时需把gmp函数放在Polygon类上面L,ine类下面,并且看情况修改pt[0]**/
    411 //    bool gcmp(const Point &ta,const Point &tb)/// 选取与最后一条确定边夹角最小的点,即余弦值最大者
    412 //    {
    413 //        double tmp=Line(pt[0],ta)/Line(pt[0],tb);
    414 //        if(Point::sgn(tmp,0)==0)
    415 //            return Point::dis(pt[0],ta)<Point::dis(pt[0],tb);
    416 //        else if(tmp>0)
    417 //            return 1;
    418 //        return 0;
    419 //    }
    420 //    void graham(Point _p[],int _n)
    421 //    {
    422 //        int cur=0;
    423 //        for(int i=1;i<_n;i++)
    424 //            if(Point::sgn(_p[cur].y,_p[i].y)>0 || (Point::sgn(_p[cur].y,_p[i].y)==0 && Point::sgn(_p[cur].x,_p[i].x)>0))
    425 //                cur=i;
    426 //        swap(_p[cur],_p[0]);
    427 //        n=0,pt[n++]=_p[0];
    428 //        if(_n==1)   return;
    429 //        sort(_p+1,_p+_n,Polygon::gcmp);
    430 //        pt[n++]=_p[1],pt[n++]=_p[2];
    431 //        for(int i=3;i<_n;i++)
    432 //        {
    433 //            while(n>1 && Point::sgn(Line(pt[n-2],pt[n-1])/Line(pt[n-2],_p[i]),0)<0)// 当凸包退化成直线时需特别注意n
    434 //                n--;
    435 //            pt[n++]=_p[i];
    436 //        }
    437 //    }
    438     //凸包旋转卡壳(注意点必须顺时针或逆时针排列)
    439     //返回值凸包直径的平方(最远两点距离的平方)
    440     double rotating_calipers()
    441     {
    442         int i = 1;
    443         double ret = 0.0;
    444         pt[n] = pt[0];
    445         for(int j = 0; j < n; j ++)
    446         {
    447             while(fabs(Point::xmult(pt[i+1],pt[j], pt[j + 1])) > fabs(Point::xmult(pt[i],pt[j], pt[j + 1])) + eps)
    448                 i = (i + 1) % n;
    449             //pt[i]和pt[j],pt[i + 1]和pt[j + 1]可能是对踵点
    450             ret = max(ret, max(Point::dis(pt[i],pt[j]), Point::dis(pt[i + 1],pt[j + 1])));
    451         }
    452         return ret;
    453     }
    454 
    455     //凸包旋转卡壳(注意点必须逆时针排列)
    456     //返回值两凸包的最短距离
    457     double rotating_calipers(Polygon &_Off)
    458     {
    459         int i = 0;
    460         double ret = 1e10;//inf
    461         pt[n] = pt[0];
    462         _Off.pt[_Off.n] = _Off.pt[0];
    463         //注意凸包必须逆时针排列且pt[0]是左下角点的位置
    464         while(_Off.pt[i + 1].y > _Off.pt[i].y)
    465             i = (i + 1) % _Off.n;
    466         for(int j = 0; j < n; j ++)
    467         {
    468             double tp;
    469             //逆时针时为 >,顺时针则相反
    470             while((tp = Point::xmult(_Off.pt[i + 1],pt[j], pt[j + 1]) - Point::xmult(_Off.pt[i], pt[j], pt[j + 1])) > eps)
    471                 i = (i + 1) % _Off.n;
    472             //(pt[i],pt[i+1])和(_Off.pt[j],_Off.pt[j + 1])可能是最近线段
    473             ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i], true));
    474             ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j + 1], true));
    475             if(tp > -eps)//如果不考虑TLE问题最好不要加这个判断
    476             {
    477                 ret = min(ret, Line(pt[j], pt[j + 1]).dis(_Off.pt[i + 1], true));
    478                 ret = min(ret, Line(_Off.pt[i], _Off.pt[i + 1]).dis(pt[j], true));
    479             }
    480         }
    481         return ret;
    482     }
    483 
    484     //-----------半平面交-------------
    485     //复杂度:O(nlog2(n))
    486     //#include <algorithm>
    487 
    488     //获取半平面交的多边形(多边形的核)
    489     //参数:向量集合[l],向量数量[ln];(半平面方向在向量左边)
    490     //函数运行后如果n[即返回多边形的点数量]为0则不存在半平面交的多边形(不存在区域或区域面积无穷大)
    491     int halfPanelCross(Line *L, int ln)
    492     {
    493         sort(L,L + ln);     //极角排序
    494         int first,last;     //双端队列的队首和队尾的下标
    495         Point *p = new Point[ln];   //p[i]为q[i]和q[i+1]的交点
    496         Line *q = new Line[ln];     //双端队列
    497         q[first=last=0] = L[0];     //双端队列初始只有一个半平面L[0]
    498         for(int i = 1; i < ln; i++)
    499         {
    500             while(first < last && !(p[last-1] < L[i])) last--;
    501             while(first < last && !(p[first] < L[i])) first++;
    502             q[++last] = L[i];
    503             if(fabs(q[last]/q[last-1]) < eps)
    504             {
    505                 if(L[i].s < q[--last]) q[last] = L[i];  //两向量平行,取内侧的一个
    506             }
    507             if(first < last) Line::crossLPt(q[last-1], q[last], p[last-1]);
    508         }
    509         while(first < last && !(p[last-1] < q[first])) last--; //删除无用平面
    510         if(last - first <= 1) return 0; //空集
    511         Line::crossLPt(q[last], q[first], p[last]);   //计算首尾两个半平面的交点
    512         int m=0;
    513         for(int i = 1; i <= last; i++) pt[m++] = p[i];  //复制到pt中
    514         return m;
    515     }
    516 };
    517 
    518 class Circle
    519 {
    520 public:
    521     Point c;//圆心
    522     double r;//半径
    523     double db, de;//圆弧度数起点, 圆弧度数终点(逆时针0-360)
    524 
    525     //-------圆---------
    526 
    527     //判断圆在多边形内
    528     bool inside(const Polygon &_Off) const
    529     {
    530         if(_Off.ahas(c) == false)
    531             return false;
    532         for(int i = 0; i < _Off.n; i ++)
    533         {
    534             Line l = Line(_Off.pt[i], _Off.pt[(i + 1) % _Off.n]);
    535             if(l.dis(c, true) < r - eps)
    536                 return false;
    537         }
    538         return true;
    539     }
    540 
    541     //判断多边形在圆内(线段和折线类似)
    542     bool has(const Polygon &_Off) const
    543     {
    544         for(int i = 0; i < _Off.n; i ++)
    545             if(Point::dis2(_Off.pt[i],c) > r * r - eps)
    546                 return false;
    547         return true;
    548     }
    549 
    550     //-------圆弧-------
    551     //圆被其他圆截得的圆弧,参数:圆[_Off]
    552     Circle operator-(Circle &_Off) const
    553     {
    554         //注意圆必须相交,圆心不能重合
    555         double d2 = Point::dis2(c,_Off.c);
    556         double d = Point::dis(c,_Off.c);
    557         double ans = acos((d2 + r * r - _Off.r * _Off.r) / (2 * d * r));
    558         Point py = _Off.c - c;
    559         double oans = atan2(py.y, py.x);
    560         Circle res;
    561         res.c = c;
    562         res.r = r;
    563         res.db = oans + ans;
    564         res.de = oans - ans + 2 * PI;
    565         return res;
    566     }
    567     //圆被其他圆截得的圆弧,参数:圆[_Off]
    568     Circle operator+(Circle &_Off) const
    569     {
    570         //注意圆必须相交,圆心不能重合
    571         double d2 = Point::dis2(c,_Off.c);
    572         double d = Point::dis(c,_Off.c);
    573         double ans = acos((d2 + r * r - _Off.r * _Off.r) / (2 * d * r));
    574         Point py = _Off.c - c;
    575         double oans = atan2(py.y, py.x);
    576         Circle res;
    577         res.c = c;
    578         res.r = r;
    579         res.db = oans - ans;
    580         res.de = oans + ans;
    581         return res;
    582     }
    583 
    584     //过圆外一点的两条切线
    585     //参数:点[_Off](必须在圆外),返回:两条切线(切线的s点为_Off,e点为切点)
    586     std::pair<Line, Line>  tangent(const Point &_Off) const
    587     {
    588         double d = Point::dis(c,_Off);
    589         //计算角度偏移的方式
    590         double angp = std::acos(r / d), ango = std::atan2(_Off.y - c.y, _Off.x - c.x);
    591         Point pl = Point(c.x + r * std::cos(ango + angp), c.y + r * std::sin(ango + angp)),
    592             pr = Point(c.x + r * std::cos(ango - angp), c.y + r * std::sin(ango - angp));
    593         return std::make_pair(Line(_Off, pl), Line(_Off, pr));
    594     }
    595 
    596     //计算直线和圆的两个交点
    597     //参数:直线[_Off](两点式),返回两个交点,注意直线必须和圆有两个交点
    598     std::pair<Point, Point> cross(Line _Off) const
    599     {
    600         _Off.pton();
    601         //到直线垂足的距离
    602         double td = fabs(_Off.a * c.x + _Off.b * c.y + _Off.c) / sqrt(_Off.a * _Off.a + _Off.b * _Off.b);
    603 
    604         //计算垂足坐标
    605         double xp = (_Off.b * _Off.b * c.x - _Off.a * _Off.b * c.y - _Off.a * _Off.c) / ( _Off.a * _Off.a + _Off.b * _Off.b);
    606         double yp = (- _Off.a * _Off.b * c.x + _Off.a * _Off.a * c.y - _Off.b * _Off.c) / (_Off.a * _Off.a + _Off.b * _Off.b);
    607 
    608         double ango = std::atan2(yp - c.y, xp - c.x);
    609         double angp = std::acos(td / r);
    610 
    611         return std::make_pair(Point(c.x + r * std::cos(ango + angp), c.y + r * std::sin(ango + angp)),
    612             Point(c.x + r * std::cos(ango - angp), c.y + r * std::sin(ango - angp)));
    613     }
    614 };
    615 
    616 class triangle
    617 {
    618 public:
    619     Point a, b, c;//顶点
    620     triangle(){}
    621     triangle(Point a, Point b, Point c): a(a), b(b), c(c){}
    622 
    623     //计算三角形面积
    624     double area()
    625     {
    626         return fabs(Point::xmult(a, b, c)) / 2.0;
    627     }
    628 
    629     //计算三角形外心
    630     //返回:外接圆圆心
    631     Point circumcenter()
    632     {
    633         Line u,v;
    634         u.s.x = (a.x + b.x) / 2;
    635         u.s.y = (a.y + b.y) / 2;
    636         u.e.x = u.s.x - a.y + b.y;
    637         u.e.y = u.s.y + a.x - b.x;
    638         v.s.x = (a.x + c.x) / 2;
    639         v.s.y = (a.y + c.y) / 2;
    640         v.e.x = v.s.x - a.y + c.y;
    641         v.e.y = v.s.y + a.x - c.x;
    642         Point ret;
    643         Line::crossLPt(u,v,ret);
    644         return ret;
    645     }
    646 
    647     //计算三角形内心
    648     //返回:内接圆圆心
    649     Point incenter()
    650     {
    651         Line u, v;
    652         double m, n;
    653         u.s = a;
    654         m = atan2(b.y - a.y, b.x - a.x);
    655         n = atan2(c.y - a.y, c.x - a.x);
    656         u.e.x = u.s.x + cos((m + n) / 2);
    657         u.e.y = u.s.y + sin((m + n) / 2);
    658         v.s = b;
    659         m = atan2(a.y - b.y, a.x - b.x);
    660         n = atan2(c.y - b.y, c.x - b.x);
    661         v.e.x = v.s.x + cos((m + n) / 2);
    662         v.e.y = v.s.y + sin((m + n) / 2);
    663         Point ret;
    664         Line::crossLPt(u,v,ret);
    665         return ret;
    666     }
    667 
    668     //计算三角形垂心
    669     //返回:高的交点
    670     Point perpencenter()
    671     {
    672         Line u,v;
    673         u.s = c;
    674         u.e.x = u.s.x - a.y + b.y;
    675         u.e.y = u.s.y + a.x - b.x;
    676         v.s = b;
    677         v.e.x = v.s.x - a.y + c.y;
    678         v.e.y = v.s.y + a.x - c.x;
    679         Point ret;
    680         Line::crossLPt(u,v,ret);
    681         return ret;
    682     }
    683 
    684     //计算三角形重心
    685     //返回:重心
    686     //到三角形三顶点距离的平方和最小的点
    687     //三角形内到三边距离之积最大的点
    688     Point barycenter()
    689     {
    690         Line u,v;
    691         u.s.x = (a.x + b.x) / 2;
    692         u.s.y = (a.y + b.y) / 2;
    693         u.e = c;
    694         v.s.x = (a.x + c.x) / 2;
    695         v.s.y = (a.y + c.y) / 2;
    696         v.e = b;
    697         Point ret;
    698         Line::crossLPt(u,v,ret);
    699         return ret;
    700     }
    701 
    702     //计算三角形费马点
    703     //返回:到三角形三顶点距离之和最小的点
    704     Point fermentPoint()
    705     {
    706         Point u, v;
    707         double step = fabs(a.x) + fabs(a.y) + fabs(b.x) + fabs(b.y) + fabs(c.x) + fabs(c.y);
    708         int i, j, k;
    709         u.x = (a.x + b.x + c.x) / 3;
    710         u.y = (a.y + b.y + c.y) / 3;
    711         while (step > eps)
    712         {
    713             for (k = 0; k < 10; step /= 2, k ++)
    714             {
    715                 for (i = -1; i <= 1; i ++)
    716                 {
    717                     for (j =- 1; j <= 1; j ++)
    718                     {
    719                         v.x = u.x + step * i;
    720                         v.y = u.y + step * j;
    721                         if (Point::dis(u,a) + Point::dis(u,b) + Point::dis(u,c) > Point::dis(v,a) + Point::dis(v,b) + Point::dis(v,c))
    722                             u = v;
    723                     }
    724                 }
    725             }
    726         }
    727         return u;
    728     }
    729 };
    730 
    731 int main(void)
    732 {
    733 
    734     return 0;
    735 }
  • 相关阅读:
    数据中台的“自动化数据治理”时代已来
    如何利用缓存机制实现JAVA类反射性能提升30倍
    快速入门开发实现订单类图片识别结果抽象解析
    Nginx专题(1):Nginx之反向代理及配置
    Github 上热门的 Spring Boot 项目实战推荐
    设计模式之命令模式(二)
    设计模式之命令模式(一)
    设计模式之单例模式(二)
    设计模式之单例模式(一)
    好的学习带给我什么
  • 原文地址:https://www.cnblogs.com/weeping/p/6363742.html
Copyright © 2020-2023  润新知