直线与线段模板
#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
#define eps 1e-8
#define zero(x) (((x) > 0 ? (x) : -(x)) < eps)
using namespace std;
struct point
{
double x, y;
};
struct line
{
point a, b;
};
//计算 cross product (P1-P0)x(P2-P0)
double xmult(point p1, point p2, point p0)
{
return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}
double xmult(double x1, double y1, double x2, double y2, double x0, double y0)
{
return (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
}
//计算 dot product (P1-P0).(P2-P0)
double dmult(point p1, point p2, point p0)
{
return (p1.x - p0.x) * (p2.x - p0.x) + (p1.y - p0.y) * (p2.y - p0.y);
}
double dmult(double x1, double y1, double x2, double y2, double x0, double y0)
{
return (x1 - x0) * (x2 - x0) + (y1 - y0) * (y2 - y0);
}
//两点距离
double distance(point p1, point p2)
{
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
double distance(double x1, double y1, double x2, double y2)
{
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
//判三点是否共线
int dots_inline(point p1, point p2, point p3)
{
return zero(xmult(p1, p2, p3));
}
// 判点是否在线段上
//判点是否在线段上,包括端点(下面为两种接口模式)
int dot_online_in(point p, line l)
{
return zero(xmult(p, l.a, l.b)) && (l.a.x - p.x) * (l.b.x - p.x) < eps && (l.a.y - p.y) * (l.b.y - p.y) < eps;
}
int dot_online_in(point p, point l1, point l2)
{
return zero(xmult(p, l1, l2)) && (l1.x - p.x) * (l2.x - p.x) < eps && (l1.y - p.y) * (l2.y - p.y) < eps;
}
//判点是否在线段上,不包括端点
int dot_online_ex(point p, line l)
{
return dot_online_in(p, l) && (!zero(p.x - l.a.x) || !zero(p.y - l.a.y)) && (!zero(p.x - l.b.x) || !zero(p.y - l.b.y));
}
//判断两点在线段的同一侧
//判两点在线段同侧,点在线段上返回 0
int same_side(point p1, point p2, line l)
{
return xmult(l.a, p1, l.b) * xmult(l.a, p2, l.b) > eps;
}
int same_side(point p1, point p2, point l1, point l2)
{
return xmult(l1, p1, l2) * xmult(l1, p2, l2) > eps;
}
//判断两点是否在线段的异侧
//判两点在线段异侧,点在线段上返回 0
int opposite_side(point p1, point p2, line l)
{
return xmult(l.a, p1, l.b) * xmult(l.a, p2, l.b) < -eps;
}
int opposite_side(point p1, point p2, point l1, point l2)
{
return xmult(l1, p1, l2) * xmult(l1, p2, l2) < -eps;
}
//求点关于直线的对称点
// 点关于直线的对称点 // by lyt
// 缺点:用了斜率
// 也可以利用"点到直线上的最近点"来做,避免使用斜率。
point symmetric_point(point p1, point l1, point l2)
{
point ret;
if (l1.x > l2.x - eps && l1.x < l2.x + eps)
{
ret.x = (2 * l1.x - p1.x);
ret.y = p1.y;
}
else
{
double k = (l1.y - l2.y) / (l1.x - l2.x);
ret.x = (2 * k * k * l1.x + 2 * k * p1.y - 2 * k * l1.y - k * k * p1.x + p1.x) / (1 + k * k);
ret.y = p1.y - (ret.x - p1.x) / k;
}
return ret;
}
//判断两线段是否相交
//常用版
//定义点
struct Point
{
double x;
double y;
};
typedef struct Point point;
//叉积
double multi(point p0, point p1, point p2)
{
return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}
//相交返回 true,否则为 false, 接口为两线段的端点
bool isIntersected(point s1, point e1, point s2, point e2)
{
return (max(s1.x, e1.x) >= min(s2.x, e2.x)) &&
(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&
(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&
(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&
(multi(s1, s2, e1) * multi(s1, e1, e2) > 0) &&
(multi(s2, s1, e2) * multi(s2, e2, e1) > 0);
}
//不常用版
//判两线段相交,包括端点和部分重合
int intersect_in(line u, line v)
{
if (!dots_inline(u.a, u.b, v.a) || !dots_inline(u.a, u.b, v.b))
return !same_side(u.a, u.b, v) && !same_side(v.a, v.b, u);
return dot_online_in(u.a, v) || dot_online_in(u.b, v) || dot_online_in(v.a, u) || dot_online_in(v.b, u);
}
int intersect_in(point u1, point u2, point v1, point v2)
{
if (!dots_inline(u1, u2, v1) || !dots_inline(u1, u2, v2))
return !same_side(u1, u2, v1, v2) && !same_side(v1, v2, u1, u2);
return dot_online_in(u1, v1, v2) || dot_online_in(u2, v1, v2) || dot_online_in(v1, u1, u2) || dot_online_in(v2, u1, u2);
}
//判两线段相交,不包括端点和部分重合
int intersect_ex(line u, line v)
{
return opposite_side(u.a, u.b, v) && opposite_side(v.a, v.b, u);
}
int intersect_ex(point u1, point u2, point v1, point v2)
{
return opposite_side(u1, u2, v1, v2) && opposite_side(v1, v2, u1, u2);
}
//求两条直线的交点
//计算两直线交点,注意事先判断直线是否平行!
//线段交点请另外判线段相交(同时还是要判断是否平行!)
point intersection(point u1, point u2, point v1, point v2)
{
point ret = u1;
double t = ((u1.x - v1.x) * (v1.y - v2.y) - (u1.y - v1.y) * (v1.x - v2.x)) / ((u1.x - u2.x) * (v1.y - v2.y) - (u1.y - u2.y) * (v1.x - v2.x));
ret.x += (u2.x - u1.x) * t;
ret.y += (u2.y - u1.y) * t;
return ret;
}
//点到直线的最近距离
point ptoline(point p, point l1, point l2)
{
point t = p;
t.x += l1.y - l2.y, t.y += l2.x - l1.x;
return intersection(p, t, l1, l2);
}
//点到线段的最近距离
point ptoseg(point p, point l1, point l2)
{
point t = p;
t.x += l1.y - l2.y, t.y += l2.x - l1.x;
if (xmult(l1, t, p) * xmult(l2, t, p) > eps)
return distance(p, l1) < distance(p, l2) ? l1 : l2;
return intersection(p, t, l1, l2);
}
凸包面积
#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
#define eps 1e-8
#define zero(x) (((x) > 0 ? (x) : -(x)) < eps)
using namespace std;
const int maxn = 1e5 + 10;
struct point
{
double x, y;
} p[maxn], P[maxn];
double X(point a, point b, point c)
{
return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
}
double len(point a, point b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
bool cmp(point a, point b)
{
if (X(p[0], a, b) > 0)
return true;
else if (X(p[0], a, b) < 0)
return false;
else
return len(p[0], a) < len(p[0], b);
}
signed main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
scanf("%lf%lf", &p[i].x, &p[i].y);
for (int i = 0; i < n; i++)
{
if (p[i].y < p[0].y || p[i].y == p[0].y && p[i].x < p[0].x)
swap(p[i], p[0]);
}
sort(p + 1, p + n, cmp);
P[0] = p[0];
P[1] = p[1];
int tot = 1;
for (int i = 2; i < n; i++)
{
while (tot > 0 && X(P[tot - 1], P[tot], p[i]) <= 0)
tot--;
P[++tot] = p[i];
}
double s = 0;
for (int i = 0; i <= tot; i++)
{
printf("%lf %lf\n", P[i].x, P[i].y);
}
for (int i = 2; i <= tot; i++)
{
s += X(P[0], P[i - 1], P[i]);
}
s /= 2;
printf("%.2lf\n", s);
return 0;
}
半平面交
#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
#define eps 1e-8
#define zero(x) (((x) > 0 ? (x) : -(x)) < eps)
using namespace std;
const int maxn = 200;
struct Point
{ //点的表示
double x, y;
Point(double _x = 0, double _y = 0)
{
x = _x;
y = _y;
}
friend Point operator+(const Point &a, const Point &b)
{
return Point(a.x + b.x, a.y + b.y);
}
friend Point operator-(const Point &a, const Point &b)
{
return Point(a.x - b.x, a.y - b.y);
}
friend Point operator==(const Point &a, const Point &b)
{
return fabs(a.x - b.x) < eps && fabs(a.y - b.y) < eps;
}
} convex[maxn];
struct V
{ //向量的表示
Point start, end;
double ang; //角度,[-180,180]
V(Point _start = Point(0, 0), Point _end = Point(0, 0), double _ang = 0)
{
start = _start;
end = _end;
ang = _ang;
}
friend V operator+(const V &a, const V &b)
{
return V(a.start + b.start, a.end + b.end);
}
friend V operator-(const V &a, const V &b)
{
return V(a.start - b.start, a.end - b.end);
}
} l[maxn], st[maxn];
struct Triangle
{
Point A, B, C;
};
int n, cnt;
double DotMul(V a, V b)
{ //点积
a.end = a.end - a.start;
b.end = b.end - b.start;
return a.end.x * b.end.x + a.end.y * b.end.y;
}
double CroMul(V a, V b)
{ //叉积 a×b
a.end = a.end - a.start;
b.end = b.end - b.start;
return a.end.x * b.end.y - b.end.x * a.end.y;
}
int IsLineInter(V l1, V l2)
{ //相交
if (max(l1.start.x, l1.end.x) >= min(l2.start.x, l2.end.x) &&
max(l2.start.x, l2.end.x) >= min(l1.start.x, l1.end.x) &&
max(l1.start.y, l1.end.y) >= min(l2.start.y, l2.end.y) &&
max(l2.start.y, l2.end.y) >= min(l1.start.y, l1.end.y))
{
if (CroMul(l2, V(l2.start, l1.start)) * CroMul(l2, V(l2.start, l1.end)) <= 0 &&
CroMul(l1, V(l1.start, l2.start)) * CroMul(l1, V(l1.start, l2.end)) <= 0)
{
return 1;
}
}
return 0;
}
Point LineInterDot(V l1, V l2)
{ //交点
Point p;
double S1 = CroMul(V(l1.start, l2.end), V(l1.start, l2.start));
double S2 = CroMul(V(l1.end, l2.start), V(l1.end, l2.end));
p.x = (l1.start.x * S2 + l1.end.x * S1) / (S1 + S2);
p.y = (l1.start.y * S2 + l1.end.y * S1) / (S1 + S2);
return p;
}
int JudgeOut(const V &x, const Point &p)
{ //点在线的左侧
return CroMul(V(x.start, p), x) > eps; //点在左侧返回0,右侧返回1
}
int Parellel(const V &x, const V &y)
{ //平行
return fabs(CroMul(x, y)) < eps;
}
void ChangeDirection()
{
for (int i = 0; i < n; ++i)
{
swap(l[i].start, l[i].end);
}
}
double CheckDirection()
{
double ans = 0;
for (int i = 0; i < n; ++i)
{ //判断是否是顺时针
ans += CroMul(V(Point(0, 0), l[i].start), V(Point(0, 0), l[i].end));
}
return ans; // ans>0逆时针,sum<0顺时针
}
int Cmp(V a, V b)
{
if (fabs(a.ang - b.ang) < eps)
{ //角度相同时,不同的边在不同的位置
//此时有两种return方式,
// return CorMul(a,V(b.end-a.start))>=0;
//左边的边在后面的位置,这样的话,进行计算的时候就可以忽略 相同角度边的影响了
return CroMul(V(b.end - a.start), V(a.end - b.start)) > eps;
//左边的边在前面的位置,要进行进行去重判断 。
}
return a.ang < b.ang;
}
double HplaneIntersection()
{
int top = 1, bot = 0;
sort(l, l + n, Cmp);
int tmp = 1;
for (int i = 1; i < n; ++i)
{
if (l[i].ang - l[i - 1].ang > eps)
{ //去重,如果该边和前面的边平行,则忽略。
l[tmp++] = l[i];
}
}
n = tmp;
st[0] = l[0];
st[1] = l[1];
for (int i = 2; i < n; ++i)
{
if (Parellel(st[top], st[top - 1]) || Parellel(st[bot], st[bot + 1]))
return 0;
while (bot < top && JudgeOut(l[i], LineInterDot(st[top], st[top - 1])))
--top;
while (bot < top && JudgeOut(l[i], LineInterDot(st[bot], st[bot + 1])))
++bot;
st[++top] = l[i];
}
// while(bot<top&&(Parellel(st[top],st[top-1])||JudgeOut(st[bot],LineInterDot(st[top],st[top-1])))) --top;
// while(bot<top&&(Parellel(st[bot],st[bot+1])||JudgeOut(st[top],LineInterDot(st[bot],st[bot+1])))) ++bot;
while (bot < top && JudgeOut(st[bot], LineInterDot(st[top], st[top - 1])))
--top;
while (bot < top && JudgeOut(st[top], LineInterDot(st[bot], st[bot + 1])))
++bot;
if (top <= bot + 1)
return 0.00;
st[++top] = st[bot];
cnt = 0;
for (int i = bot; i < top; ++i)
{
convex[cnt++] = LineInterDot(st[i], st[i + 1]);
}
double ans = 0;
convex[cnt] = convex[0];
for (int i = 0; i < cnt; ++i)
{
ans += CroMul(V(Point(0, 0), convex[i]), V(Point(0, 0), convex[i + 1]));
}
return ans / 2;
}
signed main()
{
int t;
cin >> t;
while (t--)
{
scanf("%lld", &n);
if (n == 0)
break;
scanf("%lf%lf", &l[0].end.x, &l[0].end.y);
l[n - 1].start.x = l[0].end.x;
l[n - 1].start.y = l[0].end.y;
for (int i = 1; i < n; ++i)
{
scanf("%lf%lf", &l[i].end.x, &l[i].end.y);
l[i - 1].start.x = l[i].end.x;
l[i - 1].start.y = l[i].end.y;
}
for (int i = 0; i < n; ++i)
{
l[i].ang = atan2(l[i].end.y - l[i].start.y, l[i].end.x - l[i].start.x);
}
if (CheckDirection() < 0)
ChangeDirection(); //改成逆时针
//默认从点在线的左边。
double ans = HplaneIntersection();
printf("%.3lf\n", ans);
}
}
平面最近点对
#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
#define eps 1e-8
#define zero(x) (((x) > 0 ? (x) : -(x)) < eps)
using namespace std;
const int maxn = 1e6 + 10;
const double INF = 1e20;
struct Point
{
double x;
double y;
} point[maxn];
int n;
int tmpt[maxn];
bool cmpxy(const Point &a, const Point &b)
{
if (a.x != b.x)
return a.x < b.x;
return a.y < b.y;
}
bool cmpy(const int &a, const int &b)
{
return point[a].y < point[b].y;
}
double min(double a, double b)
{
return a < b ? a : b;
}
double dis(int i, int j)
{
return sqrt((point[i].x - point[j].x) * (point[i].x - point[j].x) + (point[i].y - point[j].y) * (point[i].y - point[j].y));
}
double Closest_Pair(int left, int right)
{
double d = INF;
if (left == right)
return d;
if (left + 1 == right)
return dis(left, right);
int mid = (left + right) >> 1;
double d1 = Closest_Pair(left, mid);
double d2 = Closest_Pair(mid + 1, right);
d = min(d1, d2);
int i, j, k = 0;
//分离出宽度为d的区间
for (i = left; i <= right; i++)
{
if (fabs(point[mid].x - point[i].x) <= d)
tmpt[k++] = i;
}
sort(tmpt, tmpt + k, cmpy);
//线性扫描
for (i = 0; i < k; i++)
{
for (j = i + 1; j < k && point[tmpt[j]].y - point[tmpt[i]].y < d; j++)
{
double d3 = dis(tmpt[i], tmpt[j]);
if (d > d3)
d = d3;
}
}
return d;
}
signed main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%lf %lf", &point[i].x, &point[i].y);
sort(point, point + n, cmpxy);
printf("%.4lf\n", Closest_Pair(0, n - 1));
return 0;
}
pick定理:
-
pick定理:给定顶点均为整点的简单多边形,皮克定理说明了其面积A和内部格点数目 i,边上给点数目 b 的关系
-
拓展:在一个格点坐标系中,已知线段两端点的坐标分别为(x1,y1),(x2,y2)。则该线段经过的格点数为:gcd(|x1-x2|,|y1-y2|)+1
-
特别的,当x1=x2,经过的结点数为|y1-y2|,反之。
两直线相交
直线一般式:
直线点斜式: