一类求解第 k 大问题的做法

这个 idea 本来想出 unrd2t3 的,可惜常数有点大,就弱化成了现在的 unrd2t2。

unrd2t2也有点误判难度了呜呜。我把这题给蒋老师看,蒋老师一两分钟就秒了,而这题场上只有一个还没被叉掉的提交;而蒋老师d2t1做了几十分钟,而这题被过穿了。差点就swap(t1,t2)了!!

结论1.可以 O(n+m) 求出 n 个递增序列的第 m 大。#

不妨设 n 个有序序列找前 m 小的问题是 T(n,m)

如果 n>m,那么可以直接对第一个元素做 m-th element 删掉没用的序列。

如果 8n<m,那么我们只保留每个序列的第一个、第三个、第五个等元素,做一遍 T(n,m/2) 的问题,这样找到的元素都必然是前 m 小的。但是这样只能找到 mn 个元素,还要往后找 n 个元素,这个就是 T(n,n)。这样做,n+m 的总规模变成了原来的 7/8

否则,随机找一个序列,选出它的第一个元素,找到比这个元素小的所有元素(如果要确定性的话,可以考虑找所有序列第一个元素的中位数)。如果在某个时刻发现小的元素超过了 m 个,那么答案就大于选出元素,可以删掉所有第一个元素比他大的序列了。否则是小了,那么可以把我们找出的元素删掉。

可以发现,上述过程 n+m 每次期望都减少了 1/c 倍,其中 c 是常数,所以上述过程是线性的。

结论2. 可以 O(nlogn+mn) 区间加区间 kth#

首先,以 1/2 的概率保留每一个元素,得到一个新序列 b,观察 b 序列的 k/2-th,设其为 v

这个是类似随机游走的过程,我们找到的这个元素的 rank 和 k 的差只有 O(n)

n 个元素分一块,每个块维护块内元素 sort 后的结果。使用分散层叠算法,就可以快速找到每个块有多少个元素 v。称所有 v 的数为“被选中的元素”。

根据结论1,我们就只需要每次找到被选中的元素的前 n 大并取消选中,然后找到没被选中的元素的前 max(k减选中个数,0) 小并选中。一直做直到边界不变了为止。

如果我们要调整 t 个元素,这样做的复杂度即为 O(n+t),即 O(n)

由于我们还要递归成 n/2 的问题,所以单次操作的复杂度是 T(n)=T(n/2)+O(n),即 T(n)=O(n)

结论3. 可以 O(n) 求出 n×n 的 "递增矩阵" 的第 k#

首先调用 unrd2t2,我们发现复杂度瓶颈在于求 n 个递增数列的第 O(n) 大。

调用之前的结论1即可。

结论4. 可以 O(n) 求出 n×n 的 "递增矩阵" 的第 n#

容易发现,只有 x×yn 的位置 (x,y) 才可能成为前 n 大。

考虑把所有可能成为前 n 大的元素分到若干个矩形中。分法如下:

首先设 d=n

我们先切出矩形 [1,d]×[1,d]。接下来还有需要继续被切分的矩形 [d+1,n]×[1,d][1,d]×[d+1,n]。以第一个为例。

我们切出 [d+1,2d]×[1,nd+1],[2d+1,4d]×[1,n2d+1][4d+1,8d]×[1,n4d+1]...。不停地切分下去。

接下来,我们把这些矩形一起做 结论3 的过程。可以发现最后的复杂度是每个矩形的复杂度的加和。一个矩形 x×y(不妨 xy)贡献的复杂度应为 O(x(logyx+1))

那个 d×d 的矩形贡献的复杂度显然是 O(n)

对于剩下的矩形,复杂度为:

O(in2idlog2((2id)2n))=O(iin2i)=O(n)

综上,这个算法可以做到 O(n)(其实可以看到我unrd2t2的标算是对着多个矩阵一起做写的)。

posted @   zhoukangyang  阅读(1904)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示
主题色彩