凸性相关

事情的起因是,发现自己一直搞不懂那些“答案在凸包上”是怎么发现的,花了一些时间研究出来一个比较好用的方法,故写一篇博客记录之。顺便总结了一些常见的 trick。

凸壳定义

分为上凸壳和下凸壳。

非常形象,以上凸壳为例,就是“向上”的“凸”壳,每条线段斜率递减。

凸壳有许多非常优美的性质,后面会介绍。

求解

给出平面上 \(n\) 个点,求出这些点组成的上凸壳。

首先对这些点按横坐标排序,考虑依加入每个点,开一个单调栈维护当前凸壳上的点集。

假设栈顶点为 \(Q\),前面一个点为 \(P\),当前要加入点 \(R\)。如果 \(\vec{PQ}\times\vec{QR}>0\),那么 \(Q\) 不可能在凸壳上,弹出栈顶直到 \(\vec{PQ}\times\vec{QR}\leq 0\),加入点 \(R\)

时间复杂度 \(O(n\log n)\),瓶颈在于排序,其他部分线性。

也就是说,合并两个凸包是线性的。

常用操作

凸壳有许多非常好的性质。

比如说,两个凸壳做闵可夫斯基和之后还是凸壳;以及在凸壳上查找一个斜率切到的点是 \(O(\log n)\) 的。等等。

还有一个广为人知的性质,叫做平面整点凸包点数上界:

\(n\times n\) 平面中,不存在三点共线的整点凸包点数量级为 \(O(n^{\frac{2}{3}})\)

由于凸包相当于两个凸壳拼起来,所以凸壳点数量级也为 \(O(n^{\frac{2}{3}})\)

证明:参见 有关整点凸包边上点数数量级的证明

这些性质非常重要。这启发我们去观察出答案是否一定在一个 \(t\times t\) 平面整点点集的凸壳上,如果在,那么就有许多手段用来维护。

当然有的题因为其良好性质,不需要暴力遍历。这就更好了。

一些例题

例一

原题未找到,如果找到欢迎联系博主。

题意:

\(2\) 个人 Alice 和 Bob,每个人有 \(m\) 个物品栏,每个物品栏只能放一个物品。有 \(n\) 个物品,物品 \(i\) 的效果可以用 \(x_i,a_i,y_i,b_i\) 这四个量来表示:

如果将 \(i\) 物品放进 Alice 的第 \(x_i\) 个物品栏中,则其将给 Alice 带来 \(a_i\) 的收益;

如果将 \(i\) 物品放进 Bob 的第 \(y_i\) 个物品栏中,则其将给 Bob 带来 \(b_i\) 的收益。

你要求一个物品的分配方案,最小化 Alice 和 Bob 收益的乘积。保证存在至少一种方案使得每个物品被分配到一个人。

\(n\leq 5\times 10^5,m\leq 2n,x_i,y_i\leq n,a_i,b_i\leq 10^6,\sum_i a_i,\sum_i b_i\leq 10^9\)

先把与凸性无关的部分一笔带过:首先建立一个二分图,左部点 \(n\) 个点表示每个物品,右部点 \(2m\) 个点表示每个物品栏。根据关系建边。把每个连通块拿出来,不难发现这个连通块要么是树要么是基环树。若一个连通块是树,那么总共只有 \(O(siz)\) 种匹配的方案;若为基环树,则只有 \(2\) 种。这样我们把每个连通块的方案跑出来,变成了这样一件事:

给定一些二元组的集合,对每个集合,你可以选择一个二元组 \((a,b)\),令 Alice 的收益增加 \(a\),Bob 的收益增加 \(b\)。要最小化 Alice 和 Bob 收益的乘积。

我们把这个东西放到二维平面上考虑,相当于有一些点集,要在每个点集中选一个点,让其表示的向量加进结果向量中,最小化结果向量两维的乘积。

进行一些观察。假如说我们真的求出了所有可能的答案向量集合,先把向量集合转换回来点集 \((x_i,y_i)\)。考察我们要最优化的目标函数 \(f(x,y)=xy\)。我们不妨枚举一个值 \(k\),来判断是否存在一个点满足 \(f(x,y)\leq k\)。发现 \(xy=k\) 就是一个反比例函数,而枚举的 \(k\) 慢慢增长的过程,就是这个反比例函数在往右上扫,第一个扫到的点就是答案点。可以观察到,答案点一定在点集的下凸壳上。这一结论的根本是反比例函数的凸性。

于是现在我们只需要维护答案点集的下凸壳就行了。根据闵可夫斯基和的那套理论,直接保留每个初始点集的下凸壳,然后闵可夫斯基和合并即可。

注意这种“固定一个阈值,让最优化的目标函数逐渐扫”的思想是非常重要的。而整道题目的凸性来源于,你要最优化的目标函数的凸性。

例二 AT_iroha2019_day4_l

简要题意:

有一个数轴,初始为空。有三种操作:

  1. 给定 \(x,v\),在坐标 \(x\) 处新增一个权值为 \(v\) 的点。
  2. 给定 \(x\),删除坐标 \(x\) 上的点。
  3. 给定 \(t\),查询数轴上所有点 \(\frac{v}{|x-t|}\) 的最大值。

\(Q\leq 3\times 10^5,x,v\leq 10^9\)

先转化到二维平面上。加点就是加一个位于 \((x,v)\) 的点,查询就是查与 \((x,0)\) 连线斜率绝对值最大的点的斜率绝对值。

可以先把绝对值拆掉,变成从左往右和从右往左做两遍,是对称的。这里只讨论右边的点对左边的贡献。

跟上个题一样考虑,让目标函数扫。相当于一条经过 \((x,0)\) 的直线,斜率从 \(+\infin\) 扫到 \(0\),那么可以直接观察到能被第一个扫到的点一定在上凸壳上。

所以可以开始用数据结构维护了。具体参见 我的题解

例三

爱来自山东省集,大概没有原题。

给定一个 \(n\) 个点的 DAG,\(1\) 号点为源点。每个点的颜色是黑色或白色。\(q\) 次询问给出 \(u,a,b\),问所有从 \(1\)\(u\) 的路径中,设经过了 \(x\) 个黑点,\(y\) 个白点,\(ax+by\) 的最大值。

\(n,m\leq 3\times 10^4,q\leq 10^5,a,b> 0\)

那这题演都不演了是吧,直接把 \(f(x,y)=ax+by\) 拍你脸上了。冷静分析一下,令 \(ax+by=k\),那么可以得到 \(y=-\frac{a}{b}x+\frac{k}{b}\)。当 \(k\)\(+\infin\) 慢慢往下扫的时候,这条直线也在往下扫,扫到的第一个点肯定在上凸壳上。

所以直接按 DAG 拓扑序处理源点到每个点的凸壳,由于凸壳点数上界,预处理部分是 \(O(mn^{\frac{2}{3}})\) 的。查询的时候在凸壳上二分一下找到那个点,这一部分是 \(O(q\log n)\) 的。于是我们解决了这道题。

例四 P5114

相信做过了前三道题的你一定能很快解决这道题。

没错,就是点分治预处理出凸壳之后询问在凸壳上二分。

例五 最小乘积生成树 P5540

老生常谈的问题。

首先根据例一的结论,有可能成为答案的点一定在下凸壳上,所以只需要求下凸壳。

这里使用另外一种求凸壳的算法,叫做 quick convex 算法,具体是这样的:

  1. 找出点集中离 \(x\) 轴和 \(y\) 轴最近的两个点,记为 \(A\)\(B\),这两个点一定是下凸壳的边界的两个点。
  2. 找出 \(AB\) 直线下方,距离 \(AB\) 最远的点 \(C\)
  3. 递归到 \(AC\)\(BC\) 重复第二步求解,直到下面找不到点。

容易证明这样就找到了凸壳上的所有点。

应用到这道题上,首先找到两个初始点 \(A,B\) 是好做的,直接按 \(a\)\(b\) 排序做两遍 Kruskal 就行。

考虑怎么求 \(C\)。最大化 \(C\)\(AB\) 的距离,就是最大化 \(S_{\Delta}ABC\),也就是最小化 \(\vec{AB}\times \vec{AC}\),也就是最大化:

\[\begin{aligned} &\quad(x_B-x_A)(y_C-y_A)-(y_B-y_A)(x_C-x_A)\\ &=(x_B-x_A)y_C+(y_A-y_B)x_C+\operatorname{const}\\ \end{aligned} \]

此时我们直接把每条边边权 \((a,b)\) 设为 \((x_B-x_A)b+(y_A-y_B)a\) 跑 Kruskal 即可得到 \(C\) 点坐标。

分析一下复杂度。首先复杂度最烂不可能超过 \(O(m\log mV^{\frac{2}{3}})\)。这个题一看就卡不满,我们有结论 \(n\) 个随机点建凸包点数量级是很小的,具体可以参见 论文(我还没搞懂)。

所以这题实际上跑得飞快。

还有一些其他的相似题,比如什么最小乘积二分图,和 NOI2022 D2 T3 二次整数规划问题。这种问题核心特征就是一个经典(并非)问题套上一个“最小乘积”,使用 quick convex 算法重设权值来解决。

思考

总结一下,这类问题的特点是,最优化的目标函数往往有几何意义,可以观察到答案一定在凸壳上。而观察答案是否在凸壳上的通用方法就是拿最优化的目标函数扫整个平面,一般来说如果这个函数有凸性并且扫的方向正确,那答案也相应地在凸壳上。

然后是维护凸壳,根据凸壳点数量级的结论,可以直接暴力,也可以闵可夫斯基和合并,最后是 quick convex 凸包算法。

这其中可能有一些更深刻的道理,比如说类三维凸性的东西,但是笔者水平不足无法探究,欢迎大家提出自己的想法!

posted @ 2025-05-17 16:51  Linge_Zzzz  阅读(82)  评论(0)    收藏  举报