【学习笔记】计算几何

只写我会的。

封装好再用,注意精度。

这里定义角度 π=180,角度的值域为 [π,π]。规定从初始边顺时针旋转的角为负角,即度数为负数。

记一条直线 PQ 所夹矩形为以 PQ 为对角线的矩形。

其实就是点,线段,直线,多边形,圆的组合。

因为本身自带大常数,一般来说理论复杂度能过就不会超时。

  • 正弦定理:令 R 为三角形外接圆半径,则 asinA=2Ra 为一条边,A 为边 a 的对角。
  • 余弦定理:令 a,b,c 为三角形三边,Aa 的对角,则 a2=b2+c22bccosA。这给了我们角度与长度的换算。cosA=b2+c2a22bc

极坐标系

类似平面直角坐标系,同样有原点 O 和单位长度 1,其中定义一条自极点引出的射线极轴 OX,就建立了一个极坐标系。

A 为极坐标系上一点,极径 |OA| 定义为 A 到原点的距离,记为 ρ。以极轴为始边 OA 为终边的角 XOA 即为极角,记为 φ

如果限制 0φ2π,那么除原点外极坐标系中每一个点都可表示为 (ρ,φ) 的形式。

与平面直角坐标系的转换

A(ρ,φ) 在平面直角坐标系可表示为 A(ρcosφ,ρsinφ)

A(x,y) 在极坐标系可表示为 A(x2+y2,atan2(y,x))。其中 atan2(y,x) 是 STL 中自带的函数。

向量

既有大小又有方向的量称为向量。或者称作一个有向线段,即带有起点,长度,方向的线段。一般表示为 a 或者 AB。也记作向量 (x,y) 表示从 (0,0)(x,y) 的向量。向量的大小也称向量的模,定义为有向线段 AB 的长度。令 A(x,y),则 |A|=x2+y2

特殊的向量:

  • 零向量:模长为 0,记作 0 。方向任意。
  • 单位向量:大小为 1 的向量为该方向的单位向量。

平行向量:方向相同或者相反的两个非零向量。记作 a∣∣b。也叫共性向量。

相等向量:模相等且方向相同的向量。相对地,相反向量指模相等但方向相反的向量。

向量计算:

加减:定义向量 a(xa,ya),b(xb,yb)a+b=(xa+xb,ya+yb),ab=(xaxb,yayb)。得到的结果仍是向量。

数乘:定义向量 a(xa,ya) 和常数 kka=(kxa,kya)。得到的结果仍是向量。

点乘:定义向量 a(xa,ya),b(xb,yb)a·b=xa×xb+ya×yb=|a|×|b|cosθ(θ=a,b)。得到的结果是数值,表示 ab 上的投影长度与 b 的长度乘积。所以计算两向量的夹角 cosθ=ab|a|×|b|。用 acos 得到 θ

叉乘:定义向量 a(xa,ya),b(xb,yb)a×b=xa×ybxb×ya=|a|×|b|sinθ(θ=a,b)。得到的结果是数值,表示 ab 所夹的平行四边形的有向面积(即可以为负)

移动向量:朝一个方向向量 U 移动 k 个单位长度。算出单位向量的角度(与 x 正半轴即 (0,1)θxx+kcosθ,yy+ksinθ

有向量 A,B,则向量 AB=BA

叉乘没有交换律,但是 a×b=(b×a)

角度计算中,ab 的夹角为负,当且仅当 a 可以通过顺时针旋转 180 得到 b

旋转向量

对于向量 (x,y),其绕原点顺时 针旋转 θ 后得到的向量为 (xcosθ+ysinθ,ycosθxsinθ)。会掉非常大的精度,慎用。

存储(表示)

存储一个点(向量):直接存储坐标。

存储一条直线:以直线上任意一点 P 和一个方向向量 U 表示。直线上任意一个点可以表示为 P+kU,其中 k 为常数。

判断问题:

两向量垂直:

ab=0cosθ=0O(1)

两向量平行或相同:

A×B=0

折线段拐向:

对于点 A(xa,ya),B(xb,yb),C(xc,yc),判断 ABC 的拐弯方向。

x=(BA)×(CA),若 x=0 则三点共线,若 x>0 则往左拐,若 x<0 则往右拐。O(1)

点是否在线段上:

对于点 Q 和线段 P1(x1,y1)P2(x2,y2)。先判断 Q,P1,P2 是否三点共线,然后判断 Q 是否在 P1P2 的范围内,即 x1xqx2,y1yqy2O(1)

两线段是否相交:

对于线段 P1(x1,y1)P2(x2,y2)Q1(x3,y3)Q2(x4,y4)。先判断 P1P2 所夹矩形是否和 Q1Q2 所夹矩形相交,若不相交则线段必定不相交。否则进行跨立实验。对于跨立实验,若 [(P1P2)×(Q1P2)][(P1P2)×(Q2P2)]0 则称 P1P2 跨立 Q1Q2。判断 P1P2,Q1Q2 是否互相跨立。可以自行画图,原理就是看另一条线段的两个端点是否在这条线段的左右两侧或者位于这个线段上。

跨立实验可以解决很多线段直线之类的相交问题,这里不多写。O(1)

判断点是否在圆内

判断点到圆心的距离是否小于等于半径即可。

判断点是否在凸多边形内

可以做到 O(nlogn)O(logn)。如果凸多边形按顺序给出可以做到线性。

注意到凸多边形以最左下角为原点建立极坐标系,其边满足按极角大小排序。考虑求凸包的极角排序过程,可以直接二分找到夹住它的两个点,然后看拐向是否代表被凸包包含。

提前特判超过凸包边界的情况,即特别处理 a1,a2a1,an 这两条边。

注意到按顺序给出的多边形的预处理线性仅因为需要找到最左下角的点,所以对于一个求出的凸包无需预处理即可达到 O(logn) 判断。

判断线段、折线、多边形是否在凸多边形内

判断所有端点是否都在该多边形内。

判断圆是否在多边形内

两个条件,圆心在多边形内,圆心到多边形的最短距离大于等于半径,如何计算在后面有。O(n)n 是边数。

判断点是否在多边形内

先判断该点是否为端点或者在边上面。如果可以就能结束了。令 P(px,py)

引一条向左的射线出来,实现可以用一个保证不在多边形内的点 Q(Inf,py) 变为线段,然后对每条边做跨立,记录与多边形边的相交次数 t。如果 t 是奇数代表在多边形内。然后需要剔除一些特殊情况,如不考虑多边形的水平边(判断端点纵坐标是否相等)以及如果 PQ 与一条边的交点为端点(因为 PQ 水平所以很好判)那么仅当这个端点为这条边中纵坐标更大的点才算做统计。O(n)n 为边数。

判断线段是否在多边形内

O(n) 做法,但是不会。红温了等会再补。

判断折线、多边形是否在多边形内

对于每条边判断即可。

判断圆是否在圆内

对于圆 C1(P1,r1),C2(P2,r2),判断 C2 是否在 C1 内。如果 r2>r1 显然不可能。否则若 dis(P1,P2)r1r2 则满足。注意如果取等代表两圆相切。

计算问题

三角形重心公式

ΔABC 的重心为 (xA+xB+xC3,yA+yB+yC3)。挺好记的。

求点到直线的距离

在直线上任取两点 A,B,计算向量 X=PA,Y=BA,则距离 Δ=abs(X×Y)|Y|。相当于平行四边形的面积除以底得到高。

求点到线段的距离

特判一下取端点问题,可以用一个类似跨立的东西判断是否取端点最优,如果不是,那么问题转化为求点到直线的距离。

求点到直线的垂足

求距离,然后随便推一推得到与向量 (x,y) 垂直的向量是 (y,x),朝这个方向移动距离个单位得到垂足。

求两直线交点

先判断两直线是否平行或相等。如果平行,没有交点;在平行的基础上过同一点即为重合,有无数个交点。

令两条直线分别为 (P1,U1),(P2,U2)。设交点坐标为 P1+kU1,有 (P1+kU1P2)×U2=0,所以交点 O=P1+U2×(P2P1)U2×U1U1

求直线与圆的交点

先判断不交,若圆心到直线距离大于半径则不交。算出垂足坐标,若圆心到直线距离等于半径则只有一个交点即为垂足,否则根据勾股定理,以半径为斜边,垂径为一条直角边算出偏移量,在直线上移动偏移量得到两个交点。

求三角形面积

叉积是平行四边形的面积,我们只要求任意两个边的向量的叉积除以二就是三角形面积。

求多边形面积

S=i=1nPi×P(imodn)+1]。可以画图辅助理解。

pick 定理

对于一个顶点在格点上的多边形,令面积为 Sa 为多边形内部的点,b 为多边形边上的点,有 S=a+b21。一般是知道面积回推用。

距离

  • 曼哈顿距离:dis(A,B)=|xAxB|+|yAyB|
  • 欧几里得距离:dis(A,B)=(xAxB)2+(yAyB)2
  • 切比雪夫距离:dis(A,B)=max(|xAxB|,|yAyB|)

曼哈顿距离和切比雪夫距离可以互相转化。偷懒不写。

凸包

一个包含给定点集的最小凸多边形。首先显然所有端点都被包含在点集之中。一个凸包由上下两凸壳构成。

凸壳就是把凸包切开,然后一个凸壳内的斜率单调不增(上凸壳)或单调不减(下凸壳)。

静态求凸包

不算排序的话是 O(n) 的。

x 为第一关键字,y 为第二关键字排序,扫两边求出来上下凸壳。可以在求完一个凸壳之后记录当前的单调栈顶防止求下一个凸壳时越界。求完之后我们的栈内存的就是按顺时针或逆时针遍历的凸包,且有且仅有栈的开头结尾相同。

关于维护凸壳,我们发现凸壳的斜率是单调的,所以我们维护斜率或者说这个向量和上个向量的拐向是否符合现在维护的凸壳性质,如果不是就弹出。

还有另一种不需要用到叉积自己口胡的方法:考虑凸壳的斜率具有单调性,类似斜率优化时的方法用单调栈维护斜率弹栈。这种做法会在点的纵坐标相同时出错,因为无法直接得出垂直于 x 轴的直线斜率。解决方法是对于排序后的所有 xixi+i×eps,强制扰乱坐标使得大致关系不变的前提下不出现横坐标相同的情况。求完凸包之后因为顺序没有被打乱直接把坐标还原即可。

但是计算几何中往往出现更多的算法与极角相关,于是还有 极角排序求凸包

首先视觉上处于最右下角的点一定位于凸包内,按横坐标最小或者纵坐标最小处理均可。以这个点为原点建极坐标系,以极角为第一关键字,距离为第二关键字将其他的点排序。具体实现可以通过经过原点的拐向判断哪个在极坐标系视觉上更靠下。然后按照刚才处理的方式跑一遍单调栈即可。这种做法不用求两遍凸壳,而且和某些其他计算几何算法更加适配。

带增点凸包

set 维护凸包内的点,新增点的时候判断一下在不在凸包里面,然后看能不能更新凸包,往前往后扫一下踢掉不满足凸性的点。每个点只会被加一次删一次所以还是 O(nlogn)。还是注意上下凸包的影响要开两个 set 分开维护。

带增点删点离线凸包

学长说在线不是很可做。

cdq 分治,但是不会。咕咕咕。

平面最近点对

人类的智慧是无敌的

P7883 平面最近点对(加强加强版)里最高赞题解写道:

我们充分发扬人类智慧:
将所有点全部绕原点旋转同一个角度,然后按 x×y 排序
根据数学直觉,在随机旋转后,答案中的两个点在数组中肯定不会离得太远
所以我们只取每个点向前的 50 个点来计算答案
这样速度快得飞起,在 n=400000 时都可以在 124ms 内卡过

当然,这是对的。

分治解法

复杂度 O(nlogn)。常数大。

对于一张图,按照 x 坐标排序,我们分治求解。对于一次 solve(l,r),我们解决范围在 [l,r] 区间内的最近点对距离。

容易发现 l=r 时没有答案,l=r1 时答案可以直接出来。首先答案应该现在左右最小值,令这个值为 d。考虑 [l,mid][mid+1,r] 的合并。考虑直线 x=Xmid,首先 x 坐标在 [xd,x+d] 外的可以直接排除了,因为它们与另一边的距离一定大于 d。然后考虑对于剩下的点,每个点考虑它纵坐标往上 d 的范围围成的矩形,显然如果纵坐标相差大于 d 也可以直接排除。对于这个范围内的所有其他点暴力更新 d。我们考虑按照 y 坐标归并排序,这样每次考虑纵坐标往上的 d 时可以通过双指针做到 O(n)

正确性显然。考虑复杂度分析。对于每个点可行范围暴力更新的复杂度为 O(m)m 为范围内可行点数量,那么答案就是 O(nmlogn)。可以证明 m5,是常数级别的。不证哦,可以自己画图感性理解。

旋转卡壳

求凸包的直径。凸包的直径也可以理解成平面最远点对的距离。可以证明,对于凸包的每一条边,其与凸包点的关系是单峰的,也就是说我们可以用双指针对于每条边求出与其距离最远的点然后以这个点向两端点更新答案。

注意此时最好不要把共线的点放到凸包里面,并且特判凸包内只有两个点的情况。

半平面交

咕咕咕。

闵可夫斯基和

咕咕咕。

posted @   Wind_Leaves_ShaDow  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示