计算几何入门
计算几何
zrf 评价:不用学得特别好。
世纪难题:谁在卡(双关)。
学习目标
学会快速正确地打出暴力,防止精度爆炸。
计算几何的基本概念
向量 yyds !
有三种表示:
-
点对表示
-
复数表示
两个复数相乘的时候,辐角相加,模长相乘。
如果我们有一个向量长度是 \(1\) ,我们就可以利用这个向量来旋转。逆时针旋转 \(90°\) 的操作可以特意编一个函数来加快运行速度。
如果我们有一个向量的辐角是 \(0\) ,我们就可以利用这个向量来缩放。
范数(?)
就是长度的平方。开根号容易出现精度问题,所以可以先不开。
程序设计的艺术
OOP
利用结构体和成员函数。对象内部的数据让对象自己操作。
FP
将逻辑上的纯函数实现为纯函数。
纯函数:与全局无关的函数,只与传参有关。
zrf:日常找不到鼠标。
功能细分
即使只被调用一次,也尽量写成单独的函数,长函数名可充当注释。
拼音长函数名。
关于浮点误差
原因:double
存在有效位数的限制,导致丢失一定的精度。
计算是不存在大的误差的,而存储存在误差。
zrf 评论:微积分这个东西学得好也没有用,只能应付大学生的考试。
eps
的选取:不要滥用,精度够用就行。具体题目具体分析。
eps
的正确性:平衡正确性和不正确性。考虑两个值的误差的分布是接近 \(0\) ,所以我们以概率的角度来考虑不正确性是很低的。(经验主义结论)
使用范围
-
在比较大小的时候可以使用
eps
。 -
在一些函数的使用过程中也可以使用
eps
。 -
二分中不能使用
eps
。while(r-l>eps)
❌,超大数的低位消失后可能会导致上式死循环或直接弹出。考虑直接枚举二分次数 ⭕ 。
二分的
check
是不抗扰动的,但是最后的值是不会出现大的问题的,因为误差只是平移check
函数,尽量不使用eps
。
真实作用:处理一些存在几个位置的导数绝对值十分大的函数时利用 eps
,即函数在这个位置不抗扰动的时候。同时需要满足自变量的误差分布集中在正确值附近。
zrf :我骂我自己。
如何判断一个数是 nan
:(nan!=nan)=true
。
zrf 评论:明哥牛逼!
例题
求两个向量夹角的弧度,精度要求 \(10^{-11}\) 。
题解
sol 1 :直接求辐角,相减。
sol 2:转换成复数相除,然后求辐角。
例题
求两个空间向量的夹角的弧度,精度要求 \(10^{-11}\) 。
题解
sol 1:找出平面,转换成平面问题。
sol 2:考虑使用空间上的点积算余弦,叉积算正弦,然后分类讨论,择优选取精度高的一个函数。
一些常见的元素
点:用向量表示。
直线:用点加向量表示。
圆:圆心加半径。
两条线段是否相交:判断两次跨越。
点到直线的垂足:直接点积求投影长度。
角平分线:归一化后直接加起来,可能需要特判平角。
两条直线的交点:
- 面积法求出点在直线上的比例。
- 代数法列出等式,利用行列式解方程组,行列式是叉积的代数意义(?)。
圆和直线的交点:直接解三角形,其中可以有优化。
两圆的交点:解三角形求角,旋转向量并缩放。
两圆的公切线:
- 外公切线:将圆压缩,变成点到圆的切线。
- 内公切线:考虑一个压缩一个扩张,还是变成点到圆的切线。
tricks
极限情况
- 考虑极限情况。
- 考虑二分 or 三分。
最小圆覆盖
最小圆应该是被两或三个点卡住的,枚举即可。
corner case:只有一个点。
Red And Blue Points
考虑每一条线可以被看成被两个点一左一右卡住,所以我们就可以枚举出所有可能的直线。
但是需要考虑共线的情况,尽量不通过旋转角度的方法来实现。
zrf:只要你考虑到了,随手拿出一种方案都是对的。
有两种解决方案,
- 直接不考虑。
- 转换成一个合法解。
corner case:存在只有一边有点。
极角扫描
Red And Blue Points (plus)
自由度:若 \(x\) 个有序实数可以表示有限个元素,最小的 \(x\) 就是该元素的自由度。
一个方程可以减少一个自由度,一个未知数可以将自由度加一。自由度为 \(0\) 的元素就是有限确定的。
有向直线的自由度为 \(2\) ,极角和截距。
扫描:可以用来求最优的自由度为 \(1\) 的元素。
- 计算起始状态。
- 状态缓慢(?)转移到终止状态。
- 考虑转移的过程是由事件构成的。事件即在扫描过程中对状态产生影响的东西,是有限的。
- 我们将事件按发生的事件排序,依次处理即可。
就是类似于扫描线的思路。
枚举一个点,考虑剩下的点极角排序(用叉积来实现),这样就可以快速维护直线两侧的红蓝点个数,就直接扫描?
对于同时发生的事件,我们只需要经过一些处理让它以我们希望的方式发生就行了,或者是直接同时处理一波。
在几何意义上,等价于,但的最大优势是可以正确处理 而 的情况,而不必进行会引发除零异常的操作。
考虑我们一开始的初始状态可以逻辑上偏移一个小角度,使得一开始的状态上没有事件。
极角排序的 trick
我们考虑将 \(x\) 轴旋转一个小角度,对于在直线上方的标为 \(0\) ,否则为 \(1\) 。然后对于不同标号的,比较标号;对于同标号的,比较叉积的正负。
无题
给定 \(n\) 个点,问最多有几个点共线。
就是上面的题一样的搞法?
Pionek
首先我们考虑极角排序之后相当于一个凸包了,然后单峰性和对于左端点最优右端点也是单调的。
然后我们就做好了。
简单多边形
多边形概念:
- 简单多边形:没有边相交的多边形。
- 凸多边形:凸多边形。
如何判断一个点在简单多边形的内部。
转角法:判断向量扫过的度数和(有向的),在内部是 \(2\pi\) ,不再内部是 \(0\) 。
corner case:不能判断在多边形上,但是可以直接判断是否在多边形上。
折线法:过点引出一个直线,判断穿过了边界几次。
求面积:叉积求个和。
Get Out
先将有交的圆的圆心相连。
如果不合法,那么一定存在一个多边形将原点包住。
利用转角法,我们可以转变成判断是否存在一个环的权值是大于 \(0\) 的。
也可以利用射线法,考虑拆点,每一个点奇偶分开连边。
凸包
动态凸包
\(\text{set}\) 维护即可。
旋转卡壳
通过一对平行线卡住这个凸包,同时更新答案。这个可以通过进行斜率的排序来实现,或者双指针来实现。
两个凸包上的最远点对
我们可以通过枚举一个向量,一个点是这个向量正方向最远的点,一个点是这个向量负方向最远的点。
两个凸包上的最近点对
有很多的 \(\text{corner case}\) 。
两个凸包的公切线
首先可能存在四条。
我们考虑用两条斜率相同的直线去切这两个凸包,我们考虑到如果两个直线在一瞬间成为了公切线,那么他们此时的距离就会成为 \(0\) 。我们考虑在每一次的事件的过程中,如果两条直线的距离跨过了零点,那么说明在两次事件之间存在公切线。
半平面交
类似构建凸包的思路,但是需要特判首尾。
例 1 :Average Convex Hull
随机删除一个点,求剩余 \(n-1\) 个点形成的凸包的顶点数的期望。
我们可以求出两层的凸包,由于只删除一个点,所以最多只会露出第二层一段区间。
还有一种思路就是,我们删除一个点之后,我们只需要重新求出一个三角形内的凸包,由于发现一个点最多只会存在于 \(O(1)\) 个三角形中。
但是发现三角形的凸包处理起来很麻烦,我们就考虑重建两个相邻点区间内的所有的点。证明每一个点最多只会存在于四个区间内部。
平面最近点对
\(O(n^2)\) 的好方法。
我们先随机一个角度并旋转每一个点。
然后严格按照 \(x\) 从小到大的顺序更新答案,如果遇到 \(x\) 的距离大于当前答案了,就直接 \(\text{break}\) 。
数值积分
积分
求一个函数下方到 \(x\) 轴围成的有向面积。
我们考虑如果将 \([a,b]\) 细分成若干段长度为 \(\Delta x\) 的段,每一个小区间的函数值乘上长度的累和就是积分。
符号记为:
积分是求导的逆运算,其中 \(c\) 是常数。
考虑一开始的式子:
一元积分在集合方面的应用
函数图像下方的面积:上面的公式来搞就行了。
一般图形的面积:上积分减下积分(实际上不需要分开考虑,详见格林公式?)。
求一条曲线的长度:
求旋转体的体积:
艺术大师 zrf !
求旋转体的表面积:使用圆台的侧面积来近似。
总结:在使用积分的时候需要考虑到我们近似的图形对于答案的影响,如果影响的极限是 \(0\) 就可以使用这样的近似。
圆的面积并(BZOJ 2178)
给出 \(n\) 个圆,\(n\le 2000\),要求误差在 \(10^{-9}\) 以内。
先通过暴力判断一个圆的哪些部分是边界的一部分。
然后我们通过一些三角函数和面积计算公式暴力计算就行了?
圆弧的精确积分
弓形加梯形,就是上面的方法。
Simpson 积分公式
有一个不确定的但是可以 \(O(1)\) 计算的函数 \(f(x)\) ,求它在 \([a,b]\) 上的积分。
既没有时间保证,又没有正确性保证的算法,这是一个经验性算法。
我们用一个二次函数 \(g(x)\) 拟合这个函数?令 \(c=\frac{a+b}{2}\) ,
递归使用上面的公式,同时将误差分配给两边,就是自适应辛普森。
复合区域的面积计算
简单区域:一些可以直接运算的几何图形。
复合区域:简单区域进行逻辑运算后得到的区域。
方法:
- 求出边界。
- 求积分的和。
我们考虑还是对于每一个简单区域边界的两边是否存在于复合区域内。
这个可以利用扫描的方法来解决。
表达式树:维护计算过程的一棵树。
Airport Construction (WF 2017)
给定一个简单多边形,求一条最长的线段,使得线段上的每一个点都位于多边形内部。
考虑枚举两个点,做出过这两个点的直线,那么如果这条直线合法,那么答案就是这条直线与这个多边形最外侧的交的距离。
我们考虑判断这个直线是否合法,在不考虑贡献的情况,如果这个直线能将整个多边形划成两块,说明直线是合法的。
Cherry Orchard (BSUIR final 2015)
咕咕咕。