@Asdfre 教我
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
向量
https://book.pep.com.cn/1421001122191/mobile/index.html 第六章。
向量就是反映一个东西(如物理中的力、速度等)方向和大小的量。以 a⃗\vec aa 表示。它的大小为模长 ∣a⃗∣|\vec a|∣a∣。
显然,在 kkk 维空间内,存在 kkk 个向量,以它们作为基底,则所有向量都可以用它们表示。这个其实也很好找,取平行于每一维的任意向量各一个即可。为方便表示,每个基底向量都取模长为 111 的。
这样,我们就可以用坐标 (a1,a2,...,ak)(a_1,a_2,...,a_k)(a1 ,a2 ,...,ak ) 表示一个 kkk 维向量 a⃗\vec aa 了。
(写法不太严谨,看得懂就行)
定义向量的模长为一个数值 ∣a⃗∣=a12+a22+...+ak2|\vec a|=\sqrt{a_1^2+a_2^2+...+a_k^2}∣a∣=a12 +a22 +...+ak2 。
定义向量的加运算为一个向量 a⃗+b⃗=(a1+b1,a2+b2,...,ak+bk)\vec a+\vec b=(a_1+b_1,a_2+b_2,...,a_k+b_k)a+b=(a1 +b1 ,a2 +b2 ,...,ak +bk ),显然符合交换律、结合律。
定义向量的数乘运算为一个向量 na⃗=∑j=1na⃗=(na1,na2,na3,...,nak)n\vec a=\sum_{j=1}^n \vec a=(na_1,na_2,na_3,...,na_k)na=∑j=1n a=(na1 ,na2 ,na3 ,...,nak ),显然也符合交换律、结合律,加和数乘也符合分配律。
欸求和能这么写吗,不管了。
这些应该很容易理解吧。
我们看到 222 维向量。
定义向量 a⃗,b⃗\vec a,\vec ba,b 的点乘为一个数值 a⃗⋅b⃗=a1b1+a2b2\vec a\cdot\vec b=a_1b_1+a_2b_2a⋅b=a1 b1 +a2 b2 。如果 a⃗⋅b⃗=0\vec a\cdot\vec b=0a⋅b=0,则 a⃗\vec aa 与 b⃗\vec bb 垂直。
定义向量 a⃗,b⃗\vec a,\vec ba,b 的叉乘……二维空间下二维向量没有叉乘,但是三维下有,为一个向量 a⃗×b⃗=(a2b3−a3b2,a3b1−a1b3,a1b2−a2b1)\vec a\times\vec b=(a_2b_3-a_3b_2,a_3b_1-a_1b_3,a_1b_2-a_2b_1)a×b=(a2 b3 −a3 b2 ,a3 b1 −a1 b3 ,a1 b2 −a2 b1 )。
但是,我们可以把二维空间变成三维空间,将 a⃗\vec aa 变为 (a1,a2,0)(a_1,a_2,0)(a1 ,a2 ,0)。这样,这两个向量的叉乘就为 (0,0,a1b2−a2b1)(0,0,a_1b_2-a_2b_1)(0,0,a1 b2 −a2 b1 ) 了。我们取第三维 a1b2−a2b1a_1b_2-a_2b_1a1 b2 −a2 b1 。
这个有啥用呢?注意到,这个的绝对值刚好是两个向量移到原点围成的平行四边形的面积;如果 a⃗×b⃗<0\vec a\times \vec b\lt 0a×b<0,则 a⃗\vec aa 在 b⃗\vec bb 的逆时针方向;如果 a⃗×b⃗>0\vec a\times \vec b\gt 0a×b>0,则 a⃗\vec aa 在 b⃗\vec bb 的顺时针方向;如果 a⃗×b⃗=0\vec a\times \vec b= 0a×b=0,则 a⃗\vec aa 与 b⃗\vec bb 平行。
然后,注意到,嗯,所有计算几何问题都是向量问题。这咋注意到的(((
尝试 O(1)O(1)O(1) 解决以下问题。
判断两线段的公共点数量(0、1、无数)
取两线段端点,分别用叉积判断是否另外一条线段两个端点在这条线段同一侧。如果两条线段都在异侧,则说明有恰好 111 个公共点;如果存在一条线段在同侧且叉积 ≠0\not= 0=0,则说明没有公共点;否则说明两条线段共线。分类讨论一下位置即可。
计算两平行直线的距离
任意取 L1L_1L1 一个点,L2L_2L2 两个点,用叉积计算三个点围成的平行四边形的面积,除以 L2L_2L2 上两个点的长即可。
计算两相交直线的交点
每条线段任取两个点。
显然 CE:DE=SΔACB:SΔADBCE:DE=S_{\Delta ACB}:S_{\Delta ADB}CE:DE=SΔACB :SΔADB 。
然后通过往 CD 方向偏移即可找到 E。
异侧显然一样。
其实这些都是基础(((
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代码中,我们应避免精度误差,能不用浮点就不用浮点。
HDU1756
给定一个多边形(不一定是凸多边形),问一个点是否在它内部。
我们可以以这个点为端点,引出一条射线(为方便实现,可以用一个长度特别长的线段代替),判断这个多边形与射线有几个交点,奇数个在里面,偶数个在外面。
但是注意到如果射线与多边形有交点刚好在顶点,这个特别难讨论。所以我们需要尽量不要让射线与多边形交点为顶点。我设计的“射线”为 (x,y)→(x+∞,y+∞+1)(x,y)\rarr (x+\infty,y+\infty+1)(x,y)→(x+∞,y+∞+1),斜率为 ∞+1∞\frac{\infty+1}{\infty}∞∞+1 ,在 ∞\infty∞ 足够大的情况下不可能有 (x,y)(x,y)(x,y) 以外在数据范围下的整点,规避了这个问题。
注意要特判查询的点在边上 / 点上的情况,但这个数据好像没卡/咦
注意到代码中使用了 0 个浮点运算,大大滴好。
时间复杂度:O(nm)O(nm)O(nm)。
P2742
给定 nnn 个点,求它们构成的凸包。
G 开头神秘算法。
首先选 xxx 最小的点,如果有多个,选 yyy 最小 / 最大的点。显然该点为凸包顶点之一。为方便表示,我称它为“最小点”。
然后将其它点按照与这个点的方向排序。这里选逆时针方向。
然后用一个类似单调栈的东西,记录下当前凸包每一条线。
经过一个点时,如果它在当前凸包外面,就一直弹栈,直到到凸包里面,然后凸包点连向它。
时间复杂度:O(nlogn)O(n\log n)O(nlogn)。
CF166B
给定一个凸多边形和一个任意多边形,问是否在凸多边形内。
显然多边形在凸边形内可以转化成每个顶点都在凸多边形内,也就是说其实是 mmm 次查询点是否在凸多边形内。
显然如果是任意多边形很难做到优于 O(nm)O(nm)O(nm) 的做法,但是这是一个凸多边形。
一种做法是将 mmm 个点加入原来的凸多边形跑一遍凸包,判断新凸包是否与原凸包相同,是 O((n+m)log(n+m))O((n+m)\log (n+m))O((n+m)log(n+m)) 的,但是我们希望在线解决。
UPD:在上一题中补充了最小点的定义(其实是我瞎编的定义),去看一下。
我们可以和处理凸包差不多的方法,计算它与凸多边形的最小点的相对方向,二分出原凸多边形这个方向的前驱和后驱两个点,判断它是否在这个直线外面即可。
如图所示,G 在 BC 的外面,所以它在凸包的外面。
注意有一个 corner case:这个点的可能与最小点和另一个点三点共线,如果最小点恰好与这个点有连边会出问题,需要特判。
由于逆时针好看,所以我这里倒序输入,顺时针转逆时针了。
时间复杂度:O(n+mlogn)O(n+m\log n)O(n+mlogn)。