一类求解第 k 大问题的做法
这个 idea 本来想出 unrd2t3 的,可惜常数有点大,就弱化成了现在的 unrd2t2。
unrd2t2也有点误判难度了呜呜。我把这题给蒋老师看,蒋老师一两分钟就秒了,而这题场上只有一个还没被叉掉的提交;而蒋老师d2t1做了几十分钟,而这题被过穿了。差点就swap(t1,t2)了!!
结论1.可以 \(\mathcal O(n+m)\) 求出 \(n\) 个递增序列的第 \(m\) 大。
不妨设 \(n\) 个有序序列找前 \(m\) 小的问题是 \(T(n,m)\)。
如果 \(n>m\),那么可以直接对第一个元素做 \(\textrm{m-th element}\) 删掉没用的序列。
如果 \(8n<m\),那么我们只保留每个序列的第一个、第三个、第五个等元素,做一遍 \(T(n,m/2)\) 的问题,这样找到的元素都必然是前 \(m\) 小的。但是这样只能找到 \(m-n\) 个元素,还要往后找 \(n\) 个元素,这个就是 \(T(n,n)\)。这样做,\(n+m\) 的总规模变成了原来的 \(7/8\)。
否则,随机找一个序列,选出它的第一个元素,找到比这个元素小的所有元素(如果要确定性的话,可以考虑找所有序列第一个元素的中位数)。如果在某个时刻发现小的元素超过了 \(m\) 个,那么答案就大于选出元素,可以删掉所有第一个元素比他大的序列了。否则是小了,那么可以把我们找出的元素删掉。
可以发现,上述过程 \(n+m\) 每次期望都减少了 \(1/c\) 倍,其中 \(c\) 是常数,所以上述过程是线性的。
结论2. 可以 \(\mathcal O(n\log n+m\sqrt n)\) 区间加区间 kth
首先,以 1/2 的概率保留每一个元素,得到一个新序列 \(b\),观察 \(b\) 序列的 \(k/2\)-th,设其为 \(v\)。
这个是类似随机游走的过程,我们找到的这个元素的 rank 和 \(k\) 的差只有 \(\mathcal O(\sqrt n)\)。
每 \(\sqrt n\) 个元素分一块,每个块维护块内元素 sort 后的结果。使用分散层叠算法,就可以快速找到每个块有多少个元素 \(\le v\)。称所有 \(\le v\) 的数为“被选中的元素”。
根据结论1,我们就只需要每次找到被选中的元素的前 \(\sqrt n\) 大并取消选中,然后找到没被选中的元素的前 \(\max(k-\text{减选中个数},0)\) 小并选中。一直做直到边界不变了为止。
如果我们要调整 \(t\) 个元素,这样做的复杂度即为 \(\mathcal O(\sqrt n + t)\),即 \(\mathcal O(\sqrt n)\)。
由于我们还要递归成 n/2 的问题,所以单次操作的复杂度是 \(T(n)=T(n/2)+\mathcal O(\sqrt n)\),即 \(T(n) = \mathcal O(\sqrt n)\)。
结论3. 可以 \(\mathcal O(n)\) 求出 \(n\times n\) 的 "递增矩阵" 的第 \(k\) 大
首先调用 unrd2t2,我们发现复杂度瓶颈在于求 \(n\) 个递增数列的第 \(\mathcal O(n)\) 大。
调用之前的结论1即可。
结论4. 可以 \(\mathcal O(\sqrt n)\) 求出 \(n\times n\) 的 "递增矩阵" 的第 \(n\) 大
容易发现,只有 \(x\times y\le n\) 的位置 \((x,y)\) 才可能成为前 \(n\) 大。
考虑把所有可能成为前 \(n\) 大的元素分到若干个矩形中。分法如下:
首先设 \(d = \lfloor \sqrt n \rfloor\)。
我们先切出矩形 \([1,d]\times [1,d]\)。接下来还有需要继续被切分的矩形 \([d+1,n]\times [1,d]\) 和 \([1,d]\times [d+1,n]\)。以第一个为例。
我们切出 \([d+1,2d]\times [1,\frac{n}{d+1}],[2d+1,4d]\times [1,\frac{n}{2d+1}]\),\([4d+1,8d]\times [1,\frac{n}{4d+1}]...\)。不停地切分下去。
接下来,我们把这些矩形一起做 结论3 的过程。可以发现最后的复杂度是每个矩形的复杂度的加和。一个矩形 \(x\times y\)(不妨 \(x\le y\))贡献的复杂度应为 \(\mathcal O(x(\log \frac{y}{x}+1))\)。
那个 \(d\times d\) 的矩形贡献的复杂度显然是 \(\mathcal O(\sqrt n)\)。
对于剩下的矩形,复杂度为:
综上,这个算法可以做到 \(\mathcal O(\sqrt n)\)(其实可以看到我unrd2t2的标算是对着多个矩阵一起做写的)。