计算几何笔记

向量

将一端平移至原点,向量 \(\overrightarrow{AB}\) 可表示为 \((x, y)\)

  • 向量加定义为 \((x_1+x_2, y_1+y_2)\)
  • 向量减定义为 \((x_1-x_2, y_1-y_2)\)
  • 向量叉乘 (\(\times\)) 定义为 \(|\overrightarrow{a}||\overrightarrow{b}|\sin \theta = x_1 \times y_2-x_2 \times y_1\) (注意有先后顺序)
  • 向量点乘 (\(\cdot\)) 定义为 \(|\overrightarrow{a}||\overrightarrow{b}|\cos \theta = x_1 \times x_2+y_1 \times y_2\)

P.S. 叉乘的几何意义为有向平行四边形面积 (除 \(2\) 可计算三角形面积)

P.S. 点乘的几何意义为 \(\overrightarrow{AC}\)\(\overrightarrow{AB}\) 上的投影长度与 \(\overrightarrow{AB}\) 长度的乘积

叉乘可快速判断平面上点与直线的位置关系,记一条水平直线为 \(\overrightarrow{AB}\),对于点 \(C\)

  • \(\overrightarrow{AB} \times \overrightarrow{AC} > 0\),点 \(C\)\(AB\) 上方
  • \(\overrightarrow{AB} \times \overrightarrow{AC} = 0\),点 \(C\)\(AB\)
  • \(\overrightarrow{AB} \times \overrightarrow{AC} < 0\),点 \(C\)\(AB\) 下方

平面多边形面积公式:\(S = \frac{1}{2}|(\overrightarrow{p_n} \times \overrightarrow{p_1}) + \sum_{i=2}^{n} (\overrightarrow{p_{i-1}} \times \overrightarrow{p_{i}})|\) (多余的部分会消掉)

积分

二次函数求积分:\(\int_{l}^{r} f(x)dx = \frac{r-l}{6} (f(l)+f(r)+4f(\frac{l+r}{2}))\)

自适应辛普森法 (求数值积分):

  • 朴素想法是对于 \([l, r]\),若直接拟合为二次函数的误差可接受,则直接拟合;反之分治为 \([l, mid]\)\([mid, r]\)
  • 优化方法为判断拟合为二次函数与原函数的相似度,若可接受则拟合,反之分治
db simpson(db l, db r){
	db mid = (l+r)/2;
	return (r-l)*(f(l)+f(r)+4*f(mid))/6; //	f(x) 为被求积分的函数
}
db integral(db l, db r, db ans, db eps, int dep){
	db mid = (l+r)/2;
	db fl = simpson(l, mid), fr = simpson(mid, r);
	if (dep > 12 && fabs(fl+fr-ans) <= eps*15) return fl+fr; // 可根据时限调高 12
	return integral(l, mid, fl, eps/2, dep+1)+integral(mid, r, fr, eps/2, dep+1);
}

平面最近点对

F1:set

将所有点按横坐标为第一关键字、纵坐标为第二关键字排序,开 set 维护按纵坐标排序

\(D\) 为答案 (初始时随便找两点赋值),遍历到 \(P(x, y)\) 时:

  1. 删去所有 \(x' < x-D\) 的点 (可双指针)
  2. 在 set 中遍历 \([y-D, y+D]\) 中的点,更新答案
  3. 将点 \(P\) 加入 set 中

F2:分治

按下标分治,将 \([1, n]\) 分为 \([1, \lfloor \frac{n}{2} \rfloor]\)\([\lfloor \frac{n}{2} \rfloor, n]\)

递归边界为区间只有两点,此时可以直接算;问题在于如何合并答案

\(d\) 为左区间、右区间答案最小值:

  1. 开数组记录离分隔线距离 \(\le d\) 的点 (每次暴力做即可)
  2. 按纵坐标排序
  3. 每次向后选取 \(6\) 个点更新答案

由于实现时需要排序,可以结合归并


位置关系

点是否在线段上

设需判断点 \(P\) 是否在线段 \(AB\) 上:

  1. 要求 \(\overrightarrow{AP} \times \overrightarrow{AB} = 0\),这保证 \(P\) 在直线 \(AB\)
  2. 要求 \(\min(x_A, x_B) \le x_P \le \max(x_A, x_B)\)\(\min(y_A, y_B) \le y_P \le \max(y_A, y_B)\),这保证 \(P\) 在线段 \(AB\)

两条线段是否有交

设需判断线段 \(AB\)\(CD\) 是否相交:

  • 特判四个点在一条直线上,此时线段相交等价于矩形相交:

    • \(\min(x_A, x_B) \le \max(x_C, x_D)\)
    • \(\min(x_C, x_D) \le \max(x_A, x_B)\)
    • \(\min(y_A, y_B) \le \max(y_C, y_D)\)
    • \(\min(y_C, y_D) \le \max(y_A, y_B)\)

    P.S. 若不满足此限制则必定不相交,满足则继续判断 2-3 条限制

  • 要求 \(C, D\)\(AB\) 两侧,同时 \(A, B\)\(CD\) 两侧,即:

    • \(\overrightarrow{AB} \times \overrightarrow{AC} \ge 0\)
    • \(\overrightarrow{AB} \times \overrightarrow{AD} \le 0\)
    • \(\overrightarrow{CD} \times \overrightarrow{CA} \le 0\)
    • \(\overrightarrow{CD} \times \overrightarrow{CB} \ge 0\)
  • 还有可能 \(C, D\) 调换,此时有 (可简记为全部变号):

    • \(\overrightarrow{AB} \times \overrightarrow{AC} \le 0\)
    • \(\overrightarrow{AB} \times \overrightarrow{AD} \ge 0\)
    • \(\overrightarrow{CD} \times \overrightarrow{CA} \ge 0\)
    • \(\overrightarrow{CD} \times \overrightarrow{CB} \le 0\)

实现时可记 \(d_1 = \overrightarrow{AB} \times \overrightarrow{AC}\)\(d_2 = \overrightarrow{AB} \times \overrightarrow{AD}\)\(d_3 = \overrightarrow{CD} \times \overrightarrow{CA}\)\(d_4 = \overrightarrow{CD} \times \overrightarrow{CB}\),条件 2-3 可简化为 \(d_1 \times d_2 \le 0\)\(d_3 \times d_4 \le 0\)

点是否在多边形内

从点 \(P\) 随意引一条射线,如果:

  • 与多边形有奇数个交点,则在多边形内
  • 与多边形有偶数个交点,则在多边形外

实现上可将射线转化为线段,取 \(inf = 31415926\),使斜率为无理数,避免与多边形边重合

求两条直线交点

\(st, ed\) 表示直线

设我们要求直线 \(AC\)\(BD\) 的交点 \(O\) 的位置

不妨设 \(\overrightarrow{a}, \overrightarrow{b}, \overrightarrow{c}, \overrightarrow{d}, \overrightarrow{o}\) 为各个点的向量

所求 \(\overrightarrow{o}\) 即为 \(\overrightarrow{a} + \overrightarrow{AO}\),考虑将 \(\overrightarrow{AO}\) 变形为 \(\frac{\overrightarrow{AO}}{\overrightarrow{AC}} \times \overrightarrow{AC}\)

\(\mu = \frac{\overrightarrow{AO}}{\overrightarrow{AC}}\),我们考虑将其刻画为面积

有引理:\(\overrightarrow{AC} \times \overrightarrow{BD} = 2S_{ABCD}\),将原四边形被分割成的四个三角形各自向外扩展为一个平四,再将两向量从点 \(O\) 处劈开后分别相乘,即得证

因此,原式 \(\overrightarrow{a} + \frac{\overrightarrow{AO}}{\overrightarrow{AC}} \times \overrightarrow{AC} = \overrightarrow{a} + \mu \overrightarrow{AC} = \overrightarrow{a} + \frac{-S_{\triangle ABD}}{-S_{ABCD}} \times \overrightarrow{AC} = \overrightarrow{a} + \frac{\overrightarrow{BA} \times \overrightarrow{BD}}{\overrightarrow{BD} \times \overrightarrow{AC}} \times \overrightarrow{AC}\)

求点向直线的垂足

\(P\) 向直线 \(AB\) 的垂足为 \(H\)

  • F1:利用点乘,\(|\overrightarrow{AH}| = \frac{\overrightarrow{AB} \cdot \overrightarrow{AP}}{|\overrightarrow{AB}|}\),可以乘上 \(\overrightarrow{AB}\) 方向的单位向量
    得到 \(\overrightarrow{AH}\),为 \(\overrightarrow{AH} = \frac{\overrightarrow{AB} \cdot \overrightarrow{AP}}{|\overrightarrow{AB}|} \times \frac{\overrightarrow{AB}}{|\overrightarrow{AB}|}\)
  • F2:令 \(\overrightarrow{AB} = (x, y)\),易知 \(\overrightarrow{AB'} = (y, -x)\)\(\overrightarrow{AB}\) 垂直,令点 \(Q\) 的向量为 \(\overrightarrow{q} = \overrightarrow{p}+\overrightarrow{AB'}\),可得 \(PQ\) 垂直于 \(AB\),求交点即可

求点绕原点旋转后的点:

  • 设点 \(M(x, y)\) 绕原点逆时针旋转 \(\theta\) 角,等价于两角和差

二维凸包

凸包的定义为平面上能包含所有点的最小凸多边形

Graham 算法:

  • 找到 \(y\) 最小的点作为极点建立平面极坐标系
  • 将每个点按照极角为第一关键字,长度为第二关键字排序
  • 维护类似单调栈的结构,在加入点 \(P\) 时,若栈顶到 \(P\) 为 "向右转" 则弹栈顶,直到为 "向左转" (可以使用叉乘 \(\le 0\) 判断)

Andrew 算法:

  • 按横坐标为第一关键字,纵坐标为第二关键字排序
  • 维护类似单调栈的结构:
    • 先进行升序枚举,在加入点 \(P\) 时判断是否 "向右转",维护出下凸壳
    • 将所有除初始点外的已选点设置为 "使用过"
    • 再进行降序枚举,维护出上凸壳 (与下凸壳不冲突,直接做就好)
  • 在做完第一次后进行第二次从大到小的枚举,过程类似 (注意选过的点不再选,特别认为第一次初始点处于未选状态)

P.S. 若要求画一个封闭图形将所有点围起来、且离每个点的距离 \(\ge d\),可转化为将凸包整体外移 \(d\) 再拼上一个圆

旋转卡壳

求凸包直径:

  • 先求出给定点的凸包
  • 对于一条边 \(\overrightarrow{AB}\),叉乘求出离它最远的点 \(D\),则直径只可能是 \(AD\)\(BD\)
  • \(\overrightarrow{AB}\) 逆时针旋转得到边 \(\overrightarrow{BC}\),你发现 \(D\) 也会跟着旋转,于是双指针即可
    • 对于点 \(D \rightarrow E\),若 \(E\)\(\overrightarrow{AB}\) 的距离比 \(D\)\(\overrightarrow{AB}\) 的距离大,则更新为 \(E\)
    • 反之,说明 \(D\) 点即为所求

P.S. 做题时尝试利用 "旋转后所求信息也跟着旋转,具有单调性" 的思想

半平面交

开半平面可表示为 \(Ax+By+C > 0\) (一条直线(不含)一侧的区域),闭半平面为 \(Ax+By+C \ge 0\)

半平面交指很多半平面的交集,类似很多不等式的解集

S&I 算法:

  • 将半平面转化为向量的一侧:
    • \(B \ne 0\) 时,转化为 \((0,-\frac{C}{B}) \rightarrow (B, -\frac{C}{B}-A)\)
    • \(B = 0\) 时,转化为 \((-\frac{C}{A}, 0) \rightarrow (-\frac{C}{A}, -A)\)
  • 按照极角 (斜率) 排序,相同斜率则更靠左的放在前面 (比较起点或终点即可),这是为了保证总是按照逆时针顺序枚举向量
  • 本质上是维护一个凸壳:
    • 维护单调队列,新加入的向量显然只会影响队首和队尾先处理队尾,维护下标连续的向量的交点
    • 先处理队尾,当上一个交点 (队尾处) 在当前直线右侧,可以弹出队尾
    • 当第一个交点 (队头处) 在当前直线右侧,可以弹出队首
    • 若斜率相同,由排序顺序,可以直接跳过
    • 最终若队列中元素个数 \(\le 2\) 则无解
posted @ 2025-03-10 19:18  lzlqwq  阅读(40)  评论(0)    收藏  举报