基础计算几何小记
一开始一直不想碰这东西,学了学发现不太恶心(?
参考:
https://www.cnblogs.com/ET2006/p/geometry.html
https://www.cnblogs.com/xzyxzy/p/10033130.html
基础知识
向量加向量减不讲了。下文默认 \(a=(x_1,y_1),b=(x_2,y_2)\)
向量点积
\(a\cdot b=|a||b|\cos\left\langle a,b\right\rangle\),用坐标表示为 \(x_1y_1+x_2y_2\),可以根据点积的正负来判断两个向量的夹角。小于 \(\pi/2\) 的时候为正,大于 \(\pi/2\) 时为负,等于 \(\pi/2\) 即垂直时为 \(0\)。
向量叉积
\(a\times b=|a||b|\sin\left\langle a,b\right\rangle\),用坐标表示是 \(x_1y_2-x_2y_1\),可以理解为两个列向量构成矩阵的行列式。需要注意叉积没有交换律。它主要有三个用途:
- 判断向量共线:两向量共线当且仅当叉积为 \(0\)。
 - 判断向量 \(b\) 在向量 \(a\) 的哪边:\(a\times b>0\) 在左边,\(a\times b<0\) 在右边。
 - 求三角形面积:向量 \(a,b\) 围成的面积是 \((a\times b)/2\),进而可以求出点到直线的距离。
 
向量旋转
把 \((x,y)=(l\cos \alpha,l\sin \alpha)\) 逆时针旋转 \(\theta\) 度是 \((l\cos(\alpha+\theta),\sin(\alpha+\theta))\),用恒等变换拆开就可以了。
判断线段相交
跨立实验,对于线段 \(l_1,l_2\),如果 \(l_2\) 端点在 \(l_1\) 两边并且 \(l_1\) 端点在 \(l_2\) 两边则线段相交。判断点在线段哪边可以用叉积。
求多边形面积
假设把所有点逆时针排序,假设第 \(i\) 个点和某个参考点 \(O\) 之间的向量为 \(p_i\),则答案是 \(\sum_{i=1}^n p_i\times p_{i\bmod n+1}\)。对于四边形还有一种特殊做法,就是把对角线叉积然后除以 \(2\)。
求线段交点
假设我们要求直线 \(AB\) 和直线 \(CD\) 的交点 \(X\)。对于线段 \(AB\) 和 \(CD\) 相交的情况,我们把两条线段外面的四边形 \(ACBD\) 连起来。使用面积方法,\(AO\) 和 \(AB\) 的比值恰好是三角形 \(CDA\) 和四边形 \(ACBD\) 面积的比值。然后套用上面求面积的方法。
如果对应线段不相交也可以按照上面的做法做,不过要考虑叉积符号的问题。处理符号问题应该只要找到一种能够做相交情况的计算方法就能做所有情况了。
凸包
凸包的求法
凸包满足一条性质:逆时针顺次遍历每个端点,考虑经过 \(i,i+1\) 的向量 \(v_i\) 和经过 \(i+1,i+2\) 的 \(v_{i+1}\),\(v_{i+1}\) 一定向左偏。所以我们用单调栈维护点即可。一般在求凸包的时候,都是先加入一个肯定在凸包里的点,然后再不断插入点。显然横坐标最小和最大的点都在凸包里面。
- Graham 算法:找到横坐标最小的点,以这个点为原点对其它点极角排序。然后按照上面的方法插入。
 - Andrew 算法:把凸包拆成上面和下面,然后横坐标为第一关键字纵坐标为第二关键字排序,从前往后和从后往前扫一遍即可分别求出上半部分和下半部分。
 
判断点在凸包内
我们钦定横坐标最小的点为关键点,然后考虑求出这个点可能在关键点和凸包上某条边组成的三角形内。这个显然可以按极角排序后二分。然后需要讨论点的极角在凸包外和在和关键点相邻的两条直线上的情况。
旋转卡壳
凸包有个性质:平面最远点对一定在凸包上。旋转卡壳可以求出这个最远点对。
定义一条边的对踵点满足从这个点引一条平行于这条边的直线,直线与凸包只有一个交点。容易发现对踵点是离直线距离最远的点,且满足单调性。求全部可以双指针扫一下用叉积判垂直距离,求单个只要三分一下就可以了(因为距离是单峰的)。
求出对踵点之后发现离一个点最远的点只会是相邻边的对踵点,扫一遍求答案即可。
以上其实就是旋转卡壳的流程。实际上旋转卡壳就是对凸包单调性的运用。
凸包的闵可夫斯基和
凸包的闵可夫斯基和定义为 \(\{a+b|a\in A,b\in B\}\),说白了就是在两个凸包里面取出两个点,然后把横纵坐标加一下。
结论是闵可夫斯基和的相邻点之间的向量是两个凸包的向量,然后我们可以找到两个横坐标最小点,把这两个点加起来,然后沿着已有的向量拼成一个凸包。向量可以归并排序求,注意归并排序不能按极角做,要像求凸包的时候一样判叉积正负。
半平面交
大概是给一堆(有方向的)直线然后求在直线左边的区域的交。
我们的做法是先对直线极角排序然后增量构造。具体地,我们维护一个双端队列并维护队列中每条直线和它前驱的交点。每次加入一条直线的时候,如果队列中存在直线对应交点在当前直线的右侧,那么把它弹出去。注意我们优先弹出右侧的点,弹完右侧点之后再弹左侧点(事实上只有区域闭合了我们才会弹左侧点)。做完之后凸包上可能还会挂一小段没用的直线,把它弹出去就可以了。图解见 oi-wiki。
平面最近点对
很 nb 啊!
下面介绍一种基于分治的可以在 \(O(n\log n)\) 时间内求出平面最近点对的算法。
定义 \(\text{solve}(l,r)\) 为求出 \([l,r]\) 间最近点对的函数。每次我们先分治解决 \([l,mid]\) 和 \((mid,r]\),假设当前最近点对是 \(ans\),显然距离分治中心距离大于 \(ans\) 的点都可以删掉,对于其中一个点,纵坐标距离大于 \(ans\) 的也可以不考虑。那么我们暴力枚举没被删掉的纵坐标之差不超过 \(ans\) 的点对即可。
下面证明上面算法的复杂度正确:考虑对于右边点 \(p\),能贡献它的点的区域。可以发现是以分治中心为一条边的 \(ans\times 2ans\) 的长方形。考虑这个长方形里面有多少点,显然如果对其中每个点为圆心画一个半径为 \(ans\) 的圆,那么每个点都不被包含在其它点的圆里面。容易发现里面的点只有可能是常数个。
习题
P3217
正解
考虑枚举对边,发现两条边可以成为矩形的对边需要满足:平行、垂直、长度相等。
接下来就非常 nb 啊,考虑长度、是否平行、是否垂直为三个关键字(顺序无关)、然后横坐标为第四关键字排序。这样满足长度相等、平行、垂直的都在一个连续段里面,取连续段最左和最右的即可。
虽然但是排序实际上相当花里胡哨,对这些东西划分等价类也是可以做的,甚至可以枚举对角线,但是这玩意真的简洁的可怕。。。
总结
实在想不出来有什么可总结的点,谔谔。
P2992(容斥、前缀和)
思路
考虑枚举两个点,然后从这两个点引出一条过原点的直线,那么第三个点就在两个半平面的交里面。然后如果只枚举一个点的话,再做一遍前缀和即可。需要极角排序。
实现起来有一点细节。
正解
由于我上面没太想清楚所以写的有点复杂,题解区有这个做法但是长度是我一半。
还有一种想法是减去不过原点的三角形。可以发现对一个点,剩下两点都在它过原点的直线的一边。但是因为有三个端点,所以可能会算重复,于是考虑容斥。不过容斥的做法相当简单,把贡献挂在极角序最小的一个点上即可。具体只需要对一个点计算一侧的答案即可。
总结
事实上我是列出一个和式才意识到可以前缀和的,所以建议先把式子写出来,然后通过化式子拓展思路。
另外这个容斥摊贡献可太常见了,容斥可以有很多形式,但最终的目的都是把贡献规范到我们想要的值。
P3829
思路
容易发现圆弧长度恰好是一整个圆,直线长度恰好是点凸包长度。求个凸包就可以了。
正解
就这么做。
总结
cute problem.
P4166(枚举、旋转卡壳)
不错的入门题!
思路
容易发现点一定都在凸包上面。考虑枚举一条边,然后大胆猜想它是单峰的,三分一下?没写过不知道效果。
正解
事实上枚举对边就可以了,然后类似找对踵点的过程旋转卡壳,就可以求出两边三角形的最大值。
不过还要讨论一下凸包点数 \(\leq 3\) 的情况,这个也不难讨论。
当然这个双指针是可以 \(O(n)\) 做的,其实和上面差不多,只是维护三个指针扫。然后就没有深究。
总结
写的时候出现了一个 bug:求凸包的时候叉积写成了 \(\leq\),这样会出现贡献的情况并使你得到 \(50\) 分,原因尚不明确。
做题的时候多回头看吧,反思一下自己在那一步思考出现了局限。这里就是枚举对边和枚举对角的区别,走错了就陷进去了。
P4557(闵可夫斯基和)
这也能形式化。。。
思路
试图考虑转化无果。偶然看到哪里的题解说 \(A+(-B)\),顿时就会了。
考虑形式化原条件。相当于存在 \((x_1,y_1)\in A\),\((x2,y_2)\in B\),\((x_1,y_1)=(x_2,y_2)\),移项可得 \((x_1,y_1)-(x_2,y_2)=0\),相当于 \(\{a-b|a\in A,b\in B\}\) 包含了 \((0,0)\),发现这就是闵可夫斯基和的形式。平移也不难。
正解
就这么做(
总结
公式描述总可以在一些奇妙的地方出现。。
闵可夫斯基和可以处理这种凸包的线性叠加问题。比如说对于两个凸包 \(X,Y\),我们可以轻易求出 \(aX+bY\)。
P8101(闵可夫斯基和)
思路
刚刚上面想能不能分治求闵可夫斯基和然后这题就这么搞了。。。
发现最后的答案是 \(n\) 个凸包的闵可夫斯基和的顶点,分治求闵可夫斯基和,时间 \(O(n\log n)\)。
正解
就这么做(
总结
感觉闵可夫斯基和可以在一些奇怪的二维点问题上出现。。。不太清楚。
                    
                
                
            
        
浙公网安备 33010602011771号