4080 第k个数(二分)

1. 问题描述:

给定一个 n × m 的方格矩阵,每个方格内都有一个整数元素。其中第 i 行第 j 列的方格中的元素为 i×j(行和列都从 1 开始编号)。现在,需要你将这 n × m 个整数按照非严格单调递增的顺序一一写出。请问,你写出的第 k 个整数是多少。

输入格式

一行,三个整数 n,m,k。

输出格式

一行,输出你写出的第 k 个整数。

数据范围

前 6 个测试点满足 1 ≤ n,m ≤ 10。
所有测试点满足 1 ≤ n,m ≤ 5 × 10 ^ 5,1 ≤ k ≤ n × m。

输入样例1:

2 2 2

输出样例1:

2

输入样例2:

2 3 4

输出样例2:

3

输入样例3:

1 10 5

输出样例3:

5
来源:https://www.acwing.com/problem/content/4083/

2. 思路分析:

首先我们需要分析一下如果x是答案那么它需要满足什么样的性质,可以发现当我们将矩阵中的元素排序之后有可能多个数字是相同的,也即出现连续一段相等的数字,x左边的数字的个数一定小于k,x右边到n * m之间的数字的个数一定大于等于k个,也即x的左边是不满足要求的,x的右边是满足要求的,所以x是二段性的分界点,这样我们就可以通过二分来枚举答案。

我们需要确定一下二分枚举的细节,我们可以从1~n * m枚举,其中1~n * m是不重复的数字,相当于是一个数轴,判断当前枚举的mid是否满足条件,计算矩阵中小于等于mid的数的个数是否大于等于k,如果大于等于k说明答案在mid或者是mid的左边,下一次枚举的区间为[l,mid],否则在mid的右边,也即[mid + 1,r],所以问题就转化为如何快速计算矩阵中小于等于mid的个数,对于当前的第i行,i * j  <= mid,那么j <= mid / i,每一行小于等于mid的数的个数是可以直接计算出来的,所以我们可以枚举所有的行,可以使用O(n)的时间计算出矩阵中小于等于mid的数的个数,最后判断是否大于等于k即可,通过check函数来判断是否满足条件更新下一次枚举的区间即可。

3. 代码如下:

class Solution:
    # 判断小于等于mid的数的个数是否大于等于k
    def check(self, mid: int, n: int, m: int, k: int):
        res = 0
        for i in range(1, n + 1):
            # 注意mid // i除以i的结果可能大于m, 所以需要与m取一个min, mid / i表示当前这一行小于等于mid的数的个数
            res += min(m, mid // i)
        return res >= k

    def process(self):
        n, m, k = map(int, input().split())
        l, r = 1, n * m
        while l < r:
            mid = l + r >> 1
            # 小于等于mid的数字大于等于k说明答案在mid的左边
            if self.check(mid, n, m, k):
                r = mid
            else:
                l = mid + 1
        return r


if __name__ == "__main__":
    print(Solution().process())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值