抛物面提升变换 trick

哎呀我去总算是毕业了,来随便写点东西吧。

根据我一顿搜索,这个 trick 的名字可以叫 Lifting Transform(提升变换)。

这个博客主要讲的是面对 “圆范围” 的解决方案,使用的手段是抛物面提升。

安全须知:这个技巧是 不那么实用 的,遇到了难度也比较大,不指望赛时会遇到吧。

但是感觉好酷的,这里让我从一个例子引入。

圆范围数点

在二维平面上,给定 \(n\) 个点。有 \(q\) 次询问,每次给出一个圆,问有多少点在这个圆里面。

\(n,q \le 10^5\)

如果数据随机的话可以直接二维分块,这样每次询问可以做到 \(O(B + r/B) \approx O(\sqrt n)\)

数据不随机的话比较棘手,因为圆是一个非线性的二维范围,让我们手法一下!

我们考虑把每个点 \((x,y)\) 映射为 \((x,y,z)\),这里 \(z=x^2+y^2\)

这样以后,所有点都出现在如图的这个蓝色抛物面 \(z=x^2+y^2\) 上。

image

我们考虑询问给出的圆 \((x-a)^2+(y-b)^2 \le r^2\)

展开方程得到 \((x^2 + y^2)- 2ax - 2by + a^2 + b^2 \le r^2\)

然后用 \(z\) 替换一下,移项得到 \(z \le 2ax + 2by + (r^2 - a^2 - b^2)\),即图中绿色平面。

此时圆范围变成了三维范围里的半空间(在这个绿色平面下方的所有点),我趣范围是线性的啦!!!

这样转化以后使用 KD-tree 就可以在 \(O(n \log n + qn^{0.935})\) 时间里解决了。

看起来这个 \(T(n) = 7T(n/8) + O(1) = O(n^{\log_8 7})\) 很垃圾(容易构造卡满,只是随机卡不住)。

不过这样转化以后,就有机会使用更好的数据结构做到 \(qn^{2 \over 3}\),可惜这就不太实用了。

大家发现一个事情,就是图上的切面在映射回二维平面后是原来的那个圆(废话),这个很牛,后面再说。

判断点是否在圆内

给四个二维点 \(A,B,C,D\),问 \(D\) 是否在 \(A,B,C\) 的外接圆内部。

这个也不那么实用,具体来说就是避免了开方、除法等操作,但是代价比较大。

我们依然做刚刚的映射,这样的话我们希望判断 \(D'\) 在平面 \(A'B'C'\) 的哪一侧?答案就是

\[\text{sign} \det \begin{vmatrix} A_x& A_y& A_x^2 + A_y^2& 1 \\ B_x& B_y& B_x^2 + B_y^2& 1 \\ C_x& C_y& C_x^2 + C_y^2& 1 \\ D_x& D_y& D_x^2 + D_y^2& 1 \\ \end{vmatrix} \]

代价是非常昂贵的行列式计算,所以其实也不实用(可以先手动行变换,对最后一列展开变成 3x3)。

可以硬出一个交互题,给你一些寄存器,你只能加、减、乘、比较大小,这样出题也太糟糕了 233。

后来发现其实是有题目需要这个技巧的,放在后面了。

Delaunay 三角剖分和 Voronoi 图(的另一种求法)

\(n\) 个二维点,求三角剖分,以及与其对偶的 Voronoi 图。

常见做法是 oiwiki 上面的分治做法,这里介绍的是并不好写的另一种做法,可以见 这篇介绍

我们把点提升到蓝色抛物面以后,求三维下凸壳,然后把凸壳映射回原来的平面,得到的恰好是三角剖分。

注意因为这个抛物面是下凸的,所以其实所有点都在下凸壳上。

类似地,我们求出提升后每个点的切平面,然后对它们做半空间交,得到上半包络,这个上半包络投影下来就是 Voronoi 图。

这里怎么看复杂度都不会低于 \(O(n^2)\) 了,让我们结合下面的问题看一下可能的优化方法。

CF549E Sasha Circle

给定 \(n+m\) 个点,问是否能找到一个圆,使得前 \(n\) 个点在圆内或圆上,而后 \(m\) 个在圆外。

不需要输出方案,只需要判断 Y/N。

\(n,m \le 10^4, |V| \le 10^4\)

过于变态,参考了洛谷题解。

根据前面的观察,我们有一个重要性质:平面上的圆与不垂直且与抛物面有交的平面一一对应。

我们立刻呼之欲出的做法是,只要两组点构成的两个三维凸包没有交,那么就是 Yes,否则是 No。

但是求三维凸包/半空间交是 \(O(n^2)\) 的怎么办呢,有没有更好的做法呢?

我们提到,三角剖分和抛物面提升的三维凸包的下半部分是相互投影的关系,其实更详细的描述是这样的:

  • 下凸壳,完美对应的是平面上的标准 Delaunay 三角剖分;
  • 上凸壳,完美对应的是平面上的 Anti-Delaunay 三角剖分。

这两个剖分的性质如下:

  • 标准 Delaunay 任意三角形的外接圆内部没有其他点。倾向于最大化最小角,避免细长的三角形;
  • Anti-Delaunay 任意三角形的外接圆必须包含所有其他点。倾向于最小化最大角。

我们考虑在二维平面中,哪些点有资格参与 Anti-Delaunay 三角剖分?答案是只有位于二维凸包边界上的点才有资格。因为任何被包围在凸包内部的点,都不可能成为距离任何圆心最远的点。

有一个经典日经,对于整点凸包,这里的边数 \(E = O(V^{2 \over 3})\) 受到值域限制,见 凸包顶点个数

也就是说在本题中,三维上凸壳上的点只有 \(O(E)\) 个,并非 \(O(n)\),这是本题的优化关键。

考虑有解时,答案圆对应的平面可以是什么样子的?我们一定可以调整平面,直到将要切到下侧的那个三维凸包(贴上一条边),此时这条边对应的两个点 \(u,v\) 都在圆上。考虑贴上的意义,回到 2D 平面,圆心必然在 \(u,v\) 的中垂线上。

于是我们对 \(n\) 个点求出二维凸包,这个凸包点集的 Anti-Delaunay 三角剖分会出现在三维上凸壳中,共 \(E\) 条。在这个基础上,其余的每个点能否在圆内,相当于是对圆心的范围增加了一个限制,可以在 \(O(n+m)\) 的时间里完成 check。因此我们枚举这 \(E\) 条边,即可在 \(O(E(n+m))\) 时间里完成这部分。

这样就基本做完了这个题,现在的问题是怎么求这个 Anti-Delaunay?根据 std 的复杂度,是存在 \(O(E \log E)\) 的算法的,但是我没学过。根据洛谷题解,这里可以采取分治的办法在 \(O(E^2)\) 的时间里计算,这个好处是代码比较短,具体做法如下:

  • 任意取凸壳上的一条边,具有两个顶点 A, B。
  • 暴力枚举另一个顶点 C,使得 A, B, C 的外接圆最大。
  • 连接 AC,BC。随后递归进两侧的凸包里继续求解。

这样一来就解决了本题,总复杂度为 \(O(V^{4/3} + V^{2/3}(n+m))\)

我还搜到了能线性求 Anti-Delaunay 的算法,具体来说就是使用了随机增量法,目测比 SMAWK 之类的好写。 Chew L P. Building Voronoi diagrams for convex polygons in linear expected time[J]. 1990.

posted @ 2026-06-07 17:01  Aurora5090  阅读(40)  评论(0)    收藏  举报

再次右键以切换宽度