计算几何笔记
向量
将一端平移至原点,向量 \(\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)\) 时:
- 删去所有 \(x' < x-D\) 的点 (可双指针)
- 在 set 中遍历 \([y-D, y+D]\) 中的点,更新答案
- 将点 \(P\) 加入 set 中
F2:分治
按下标分治,将 \([1, n]\) 分为 \([1, \lfloor \frac{n}{2} \rfloor]\) 与 \([\lfloor \frac{n}{2} \rfloor, n]\)
递归边界为区间只有两点,此时可以直接算;问题在于如何合并答案
令 \(d\) 为左区间、右区间答案最小值:
- 开数组记录离分隔线距离 \(\le d\) 的点 (每次暴力做即可)
- 按纵坐标排序
- 每次向后选取 \(6\) 个点更新答案
由于实现时需要排序,可以结合归并
位置关系
点是否在线段上
设需判断点 \(P\) 是否在线段 \(AB\) 上:
- 要求 \(\overrightarrow{AP} \times \overrightarrow{AB} = 0\),这保证 \(P\) 在直线 \(AB\) 上
- 要求 \(\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\) 则无解

浙公网安备 33010602011771号