//上交计算几何算法
/****************************************
* COMPUTATIONAL GEOMETRY ROUTINES
* WRITTEN BY : LIU Yu (C) 2003
****************************************/
// 叉乘
// 两个点的距离
// 点到直线距离
// 返回直线 Ax + By + C =0 的系数
// 线段
// 圆
// 两个圆的公共面积
// 矩形
// 根据下标返回多边形的边
// 两个矩形的公共面积
// 多边形 ,逆时针或顺时针给出x,y
// 多边形顶点
// 多边形的边
// 多边形的周长
// 判断点是否在线段上
// 判断两条线断是否相交,端点重合算相交
// 判断两条线断是否平行
// 判断两条直线断是否相交
// 直线相交的交点
// 判断是否简单多边形
// 求多边形面积
// 判断是否在多边形上
// 判断是否在多边形内部
// 点阵的凸包,返回一个多边形
// 最近点对的距离
#include <cmath>
#include <cstdio>
#include <memory>
#include <algorithm>
#include <iostream>
using namespace std;
typedef double TYPE;
#define Abs(x) (((x)>0)?(x):(-(x)))
#define Sgn(x) (((x)<0)?(-1):(1))
#define Max(a,b) (((a)>(b))?(a):(b))
#define Min(a,b) (((a)<(b))?(a):(b))
#define Epsilon 1e-10
#define Infinity 1e+10
#define Pi 3.14159265358979323846
TYPE Deg2Rad(TYPE deg)
{
return (deg * Pi / 180.0);
}
TYPE Rad2Deg(TYPE rad)
{
return (rad * 180.0 / Pi);
}
TYPE Sin(TYPE deg)
{
return sin(Deg2Rad(deg));
}
TYPE Cos(TYPE deg)
{
return cos(Deg2Rad(deg));
}
TYPE ArcSin(TYPE val)
{
return Rad2Deg(asin(val));
}
TYPE ArcCos(TYPE val)
{
return Rad2Deg(acos(val));
}
TYPE Sqrt(TYPE val)
{
return sqrt(val);
}
struct POINT
{
TYPE x;
TYPE y;
TYPE z;
POINT() : x(0), y(0), z(0) {};
POINT(TYPE _x_, TYPE _y_, TYPE _z_ = 0) : x(_x_), y(_y_), z(_z_) {};
};
// cross product of (o->a) and (o->b)// 叉乘
TYPE Cross(const POINT & a, const POINT & b, const POINT & o)
{
return (a.x - o.x) * (b.y - o.y) - (b.x - o.x) * (a.y - o.y);
}
// planar points' distance// 两个点的距离
TYPE Distance(const POINT & a, const POINT & b)
{
return Sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z
- b.z));
}
struct LINE
{
POINT a;
POINT b;
LINE() {};
LINE(POINT _a_, POINT _b_) : a(_a_), b(_b_) {}};
//点到直线距离
double PointToLine(POINT p0 ,POINT p1 ,POINT p2 ,POINT &cp)
{
double d = Distance(p1 ,p2);
double s = Cross(p1 ,p2 ,p0) / d;
cp.x = p0.x + s*( p2.y-p1.y) / d;
cp.y = p0.y - s*( p2.x-p1.x) / d;
return Abs(s);
}
// 返回直线 Ax + By + C =0 的系数
void Coefficient(const LINE & L, TYPE & A, TYPE & B, TYPE & C)
{
A = L.b.y - L.a.y;
B = L.a.x - L.b.x;
C = L.b.x * L.a.y - L.a.x * L.b.y;
}
void Coefficient(const POINT & p,const TYPE a,TYPE & A,TYPE & B,TYPE & C)
{
A = Cos(a); // 线段
B = Sin(a);
C = - (p.y * B + p.x * A);
}
struct SEG
{
POINT a;
POINT b;
SEG() {};
SEG(POINT _a_, POINT _b_):a(_a_),b(_b_) {};
};
// 圆
struct CIRCLE
{
TYPE x;
TYPE y;
TYPE r;
CIRCLE() {}
CIRCLE(TYPE _x_, TYPE _y_, TYPE _r_) : x(_x_), y(_y_), r(_r_) {}};
POINT Center(const CIRCLE & circle)
{
return POINT(circle.x, circle.y);
}
TYPE Area(const CIRCLE & circle)
{
return Pi * circle.r * circle.r; //两个圆的公共面积
}
TYPE CommonArea(const CIRCLE & A, const CIRCLE & B)
{
TYPE area = 0.0;
const CIRCLE & M = (A.r > B.r) ? A : B;
const CIRCLE & N = (A.r > B.r) ? B : A;
TYPE D = Distance(Center(M), Center(N));
if ((D < M.r + N.r) && (D > M.r - N.r))
{
TYPE cosM = (M.r * M.r + D * D - N.r * N.r) / (2.0 * M.r * D);
TYPE cosN = (N.r * N.r + D * D - M.r * M.r) / (2.0 * N.r * D);
TYPE alpha = 2.0 * ArcCos(cosM);
TYPE beta = 2.0 * ArcCos(cosN);
TYPE TM = 0.5*M.r*M.r*Sin(alpha);
TYPE TN=0.5 * N.r * N.r * Sin(beta);
TYPE FM = (alpha / 360.0) * Area(M);
TYPE FN = (beta / 360.0) * Area(N);
area = FM + FN - TM - TN;
}
else if (D <= M.r - N.r)
{
area = Area(N);
}
return area;
}
// 矩形
// 矩形的线段
// 2
// --------------- b
// | |
// 3 | | 1
// a ---------------
// 0
struct RECT
{
POINT a; // 左下点 POINT b; // 右上点
RECT() {};
RECT(const POINT & _a_, const POINT & _b_)
{
a = _a_;
b = _b_;
}
};
//根据下标返回多边形的边
SEG Edge(const RECT & rect, int idx)
{
SEG edge;
while (idx < 0) idx += 4;
switch (idx % 4)
{
case 0:
edge.a = rect.a;
edge.b = POINT(rect.b.x, rect.a.y);
break;
case 1:
edge.a = POINT(rect.b.x, rect.a.y);
edge.b = rect.b;
break;
case 2:
edge.a = rect.b;
edge.b = POINT(rect.a.x, rect.b.y);
break;
case 3:
edge.a = POINT(rect.a.x, rect.b.y);
edge.b = rect.a;
break;
default:
break;
}
return edge;
}
TYPE Area(const RECT & rect)
{
return (rect.b.x - rect.a.x) * (rect.b.y - rect.a.y);
}
// 两个矩形的公共面积
TYPE CommonArea(const RECT & A, const RECT & B)
{
TYPE area = 0.0;
POINT LL(Max(A.a.x, B.a.x), Max(A.a.y, B.a.y));
POINT UR(Min(A.b.x, B.b.x), Min(A.b.y, B.b.y));
if ((LL.x <= UR.x) && (LL.y <= UR.y))
{
area = Area(RECT(LL, UR));
}
return area;
}// 多边形 ,逆时针或顺时针给出x,y
struct POLY
{
int n; //n个点 TYPE * x; //x,y为点的指针,首尾必须重合 TYPE * y;
POLY() : n(0), x(NULL), y(NULL) {};
POLY(int _n_, const TYPE * _x_, const TYPE * _y_)
{
n = _n_;
x = new TYPE[n + 1];
memcpy(x, _x_, n*sizeof(TYPE));
x[n] = _x_[0];
y = new TYPE[n + 1];
memcpy(y, _y_, n*sizeof(TYPE));
y[n] = _y_[0];
}
};//多边形顶点
POINT Vertex(const POLY & poly, int idx)
{
idx %= poly.n; //多边形的边
return POINT(poly.x[idx], poly.y[idx]);
}
SEG Edge(const POLY & poly, int idx)
{
idx %= poly.n;
return SEG(POINT(poly.x[idx], poly.y[idx]),
POINT(poly.x[idx + 1], poly.y[idx + 1]));
} //多边形的周长
TYPE Perimeter(const POLY & poly)
{
TYPE p = 0.0;
for (int i = 0; i < poly.n; i++)
p = p + Distance(Vertex(poly, i), Vertex(poly, i + 1));
return p;
}
bool IsEqual(TYPE a, TYPE b)
{
return (Abs(a - b) < Epsilon);
}
bool IsEqual(const POINT & a, const POINT & b)
{
return (IsEqual(a.x, b.x) && IsEqual(a.y, b.y));
}
bool IsEqual(const LINE & A, const LINE & B)
{
TYPE A1, B1, C1;
TYPE A2, B2, C2;
Coefficient(A, A1, B1, C1);
Coefficient(B, A2, B2, C2);
return IsEqual(A1 * B2, A2 * B1) &&
IsEqual(A1 * C2, A2 * C1) && IsEqual(B1 * C2, B2 * C1);
}
// 判断点是否在线段上
bool IsOnSeg(const SEG & seg, const POINT & p)
{
return (IsEqual(p, seg.a) || IsEqual(p, seg.b)) ||
(((p.x - seg.a.x) * (p.x - seg.b.x) < 0 || (p.y - seg.a.y) * (p.y - seg.b.y) < 0) &&
(IsEqual(Cross(seg.b, p, seg.a), 0)));
}
//判断两条线断是否相交,端点重合算相交
bool IsIntersect(const SEG & u, const SEG & v)
{
return (Cross(v.a, u.b, u.a) * Cross(u.b, v.b, u.a) >= 0) &&
(Cross(u.a, v.b, v.a) * Cross(v.b, u.b, v.a) >= 0) &&(Max(u.a.x, u.b.x)>=Min(v.
a.x, v.b.x))&&(Max(v.a.x, v.b.x)>= Min(u.a.x,u.b.x))&&(Max(u.a.y, u.b.y)>=Min(
v.a.y, v.b.y))&&(Max(v.a.y, v.b.y)>=Min(u.a.y, u.b.y));
}
//判断两条线断是否平行
bool IsParallel(const LINE & A, const LINE & B)
{
TYPE A1, B1, C1;
TYPE A2, B2, C2;
Coefficient(A, A1, B1, C1);
Coefficient(B, A2, B2, C2);
return (A1*B2== A2*B1) &&((A1 * C2 != A2 * C1) || (B1 * C2 != B2 * C1));
}
//判断两条直线断是否相交
bool IsIntersect(const LINE & A, const LINE & B)
{
return !IsParallel(A, B); //直线相交的交点
}
POINT Intersection(const LINE & A, const LINE & B)
{
TYPE A1, B1, C1;
TYPE A2, B2, C2;
Coefficient(A, A1, B1, C1);
Coefficient(B, A2, B2, C2);
POINT I(0, 0);
I.x = - (B2 * C1 - B1 * C2) / (A1 * B2 - A2 * B1);
I.y = (A2 * C1 - A1 * C2) / (A1 * B2 - A2 * B1);
return I;
}
bool IsInCircle(const CIRCLE & circle, const RECT & rect)
{
return (circle.x - circle.r >= rect.a.x) &&
(circle.x + circle.r <= rect.b.x) &&(circle.y - circle.r >= rect.a.y) &&
(circle.y + circle.r <= rect.b.y);
}
//判断是否简单多边形
bool IsSimple(const POLY & poly)
{
if (poly.n < 3) return false;
SEG L1, L2;
for (int i = 0; i < poly.n - 1; i++)
{
L1 = Edge(poly, i);
for (int j = i + 1; j < poly.n; j++)
{
L2 = Edge(poly, j);
if (j == i+1)
{
if (IsOnSeg(L1, L2.b)||IsOnSeg(L2, L1.a)) return false;
}
else if (j == poly.n - i - 1)
{
if (IsOnSeg(L1, L2.a) || IsOnSeg(L2, L1.b)) return false;
}
else
{
if (IsIntersect(L1, L2)) return false; // for i
}
} // for j
}
return true;
}
//求多边形面积
TYPE Area(const POLY & poly)
{
if (poly.n < 3) return TYPE(0);
double s = poly.y[0] * (poly.x[poly.n - 1] - poly.x[1]);
for (int i = 1; i < poly.n; i++)
{
s += poly.y[i] * (poly.x[i - 1] - poly.x[(i + 1) % poly.n]);
}
return s/2;
}
//判断是否在多边形上
bool IsOnPoly(const POLY & poly, const POINT & p)
{
for (int i = 0; i < poly.n; i++)
{
if (IsOnSeg(Edge(poly, i), p)) return true;
}
return false;
}
//判断是否在多边形内部
bool IsInPoly(const POLY & poly, const POINT & p)
{
SEG L(p, POINT(Infinity, p.y));
int count = 0;
for (int i = 0; i < poly.n; i++)
{
SEG S = Edge(poly, i);
if (IsOnSeg(S, p))
{
return false; //如果想让 在poly上则返回 true,则改为true
}
if (!IsEqual(S.a.y, S.b.y))
{
POINT & q = (S.a.y > S.b.y)?(S.a):(S.b);
if (IsOnSeg(L, q))
{
++count;
}
else if(!IsOnSeg(L,S.a)&&!IsOnSeg(L,S.b)&&IsIntersect(S,L))
{
++count;
}
}
}
return (count % 2 != 0);
}
// 点阵的凸包,返回一个多边形
POLY ConvexHull(const POINT * set, int n) // 不适用于点少于三个的情况
{
POINT * points = new POINT[n];
memcpy(points, set, n * sizeof(POINT));
TYPE * X = new TYPE[n];
TYPE * Y = new TYPE[n];
int i, j, k = 0, top = 2;
for(i = 1; i < n; i++)
{
if((points[i].y<points[k].y)||((points[i].y==points[k].y)&&
(points[i].x<points[k].x)))
{
k = i;
}
}
std::swap(points[0], points[k]);
for (i = 1; i < n - 1; i++)
{
k = i;
for (j = i + 1; j < n; j++)
{
if ((Cross(points[j], points[k], points[0]) >0)||((Cross(points[j], points[k],
points[0]) == 0) && (Distance(points[0], points[j])<Distance(points[0], points[k]
))))
{
k = j;
}
}
std::swap(points[i], points[k]);
}
X[0] = points[0].x;
Y[0] = points[0].y;
X[1] = points[1].x;
Y[1] = points[1].y;
X[2] = points[2].x;
Y[2] = points[2].y;
for (i = 3; i < n; i++)
{
while(Cross(points[i],POINT(X[top],Y[top]),POINT(X[top
-1],Y[top-1]))>=0)
{
top--;
}
++top;
X[top] = points[i].x;
Y[top] = points[i].y;
}
delete [] points;
POLY poly(++top, X, Y);
delete [] X;
delete [] Y;
return poly;
}
//最近点对的距离, Written By PrincessSnow
#define MAXN 100000
POINT pt[MAXN];
bool cmp(POINT n1, POINT n2)
{
return (n1.x<n2.x || n1.x==n2.x && n1.y<n2.y);
}
double Get(double dis, int mid, int start, int end)
{
int s=mid, e=mid, i, j;
double t;
while(s > start && pt[mid].x - pt[s].x <= dis) s--;
while(e < end && pt[e].x - pt[mid].x <= dis) e++;
for(i=s; i <= e; i++)
for(j=i+1; j <= e && j <= i+7; j++)
{
t = Distance(pt[i], pt[j]);
if(t < dis) dis=t;
}
return dis;
}
double ClosestPairDistance(int start, int end)
{
int m = end-start+1, mid, i;
double t1, t2, dis=-1, t;
if(m <= 3)
{
for(i=start; i < end; i++)
{
t = Distance(pt[i] , pt[i+1]);
if(t < dis || dis == -1) dis = t;
}
t = Distance(pt[start] , pt[end]);
if(t < dis) dis=t;
return dis;
}
if(m%2 == 0) mid = start + m/2 - 1;
else mid = start + m/2;
if(m%2 == 0)
{
t1 = ClosestPairDistance(start, mid);
t2 = ClosestPairDistance(mid+1, end);
}
else
{
t1 =ClosestPairDistance(start,mid);
t2=ClosestPairDistance(mid+1,end);
}
if(t1 < t2) dis = t1;
else dis = t2;
dis = Get(dis, mid, start, end);
return dis;
}
//1. 球面上两点最短距离
// 计算圆心角lat 表示纬度, -90 <= w <= 90, lng 表示经度
// 返回两点所在大圆劣弧对应圆心角, 0 <= angle <= pi
double angle(double lng1, double lat1, double lng2, double lat2)
{
double dlng = fabs(lng1 - lng2) * pi / 180;
while(dlng >= pi+pi) dlng -= pi+pi;
if(dlng > pi) dlng = pi + pi - dlng;
lat1 *= pi / 180, lat2 *= pi / 180;
return acos( cos(lat1)*cos(lat2)*cos(dlng) + sin(lat1)*sin(lat2) );
}
// 计算距离, r为球半径
double line_dist(double r, double lng1, double lat1, double lng2, double lat2)
{
double dlng = fabs(lng1 - lng2) * pi / 180;
while(dlng >= pi+pi) dlng -= pi+pi;
if(dlng > pi) dlng = pi + pi - dlng;
lat1 *= pi / 180, lat2 *= pi / 180;
return r*sqrt(2-2*( cos(lat1)*cos(lat2)*cos(dlng)+ sin(lat1)*sin(lat2)) );
}
// 计算球面距离, r为球半径
double sphere_dist(double r, double lng1,double lat1,double lng2, double lat2)
{
return r * angle(lng1, lat1, lng2, lat2);
}
//2. 三点求圆心坐标
double GetRadiusBy3Points(double x1, double y1,
double x2, double y2, double x3, double y3, double &x, double &y)
{
// 由 ( x - x1 )^2 + ( y - y1 )^2 = ( x - x2 )^2 + ( y - y2 )^2 得
// 2*( x2 - x1 )*x + 2*( y2 - y1 )*y = x2^2 - x1^2 + y2^2 - y1^2
// 同理得
// 2*( x3 - x2 )*x + 2*( y3 - y2 )*y = x3^2 - x2^2 + y3^2 - y2^2
// 由行列式解方程得 x , y
double a11, a12, a21, a22, b1, b2;
double d, d1, d2 ;
a11 = 2 * ( x3 - x2 );
a12 = 2 * ( y3 - y2 );
a21 = 2 * ( x2 - x1 );
a22 = 2 * ( y2 - y1 );
b1 = x3*x3 - x2*x2 + y3*y3 - y2*y2;
b2 = x2*x2 - x1*x1 + y2*y2 - y1*y1;
d = a11*a22 - a12*a21;
d1 = b1*a22 - a12*b2;
d2 = a11*b2 - b1*a21;
// x , y 是圆心坐标
x = d1 / d;
y = d2 / d;
return (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y);
}
//
//
//3. 三角形几个重要的点
//设三角形的三条边为a, b, c,且不妨假设a <= b <= c
//三角形的面积可以根据海伦公式算得,如下:
//s = sqrt(p * (p - a) * (p - b) * (p - c)), p = (a + b + c) / 2
//1. 费马点(该点到三角形三个顶点的距离之和最小)
//有个有趣的结论:若三角形的三个内角均小于120度,
//那么该点连接三个顶点形成的三个角均为120度;若三角形存在一个内角
//大于120度,则该顶点就是费马点)
//计算公式如下:
//若有一个内角大于120度(这里假设为角C),则距离为a + b
//若三个内角均小于120度,则距离为
//sqrt((a * a + b * b + c * c + 4 * sqrt(3.0) * s) / 2),其中
//2.内心----角平分线的交点
//令x = (a + b - c) / 2, y = (a - b + c) / 2, z = (-a + b + c) / 2, h = s / p
// 计算公式为 sqrt(x * x + h * h) + sqrt(y * y + h * h) + sqrt(z * z + h * h)
//3.重心----中线的交点
//ACM算法模板集
// - 46 -
//计算公式如下:
//2.0 / 3 * (sqrt((2 * (a * a + b * b) - c * c) / 4)
// + sqrt((2 * (a * a + c * c) - b * b) / 4)
// + sqrt((2 * (b * b + c * c) - a * a) / 4))
//4.垂心----垂线的交点
//计算公式如下:
//3 * (c / 2 / sqrt(1 - cosC * cosC))