Leetcode 3621. Number of Integers With Popcount-Depth Equal to K I

1. 解题思路

这一题要分两步进行处理。

显然,不难发现每次变换之后得到的新的数字的最大值也就是当前数字的最大位数,而由于给定的数字不会超过 1 0 15 < 2 50 10^{15} < 2^{50} 1015<250,因此,不难注意到,对于任意给定的 n n n,要使其在 k k k次操作之后恰好变为 1 1 1,那么对应的数字 p 1 p_1 p1的范围一定在 0 0 0 50 50 50之间。

因此,我们只需要先遍历一下 1 1 1 50 50 50的所有值,看一下哪些数恰好需要 k − 1 k-1 k1次操作变为 1 1 1,然后我们只需要考察有多少不大于 n n n的数能够变成这些目标值即可。

对于前者,我们可以通过遍历找到。而对于后者,问题事实上就变成了:

  • 对于任意确定的 k k k,在不大于 n n n的自然数当中,一共有多少数字其二进制表示当中恰好有 k k k 1 1 1

这个只需要用一个迭代就能解决了:

  • 首先如果 n n n的位数 p p p小于 k k k,那么显然无法成立;
  • 然后考察最大位,如果最大位取 0 0 0,那么我们只需要在剩下的 p − 1 p-1 p1个位置上任意放置 k k k 1 1 1即可;
  • 最后,如果最大位上取 1 1 1,那么问题就退化为了在不大于 n − 2 p − 1 n-2^{p-1} n2p1的数当中一共有多少个数其二进制位上的 1 1 1的个数恰好为 k − 1 k-1 k1个。

2. 代码实现

给出python代码实现如下:

class Solution:
    def popcountDepth(self, n: int, k: int) -> int:
        m = len(bin(n)[2:])
        if k == 0:
            return 1
        elif k == 1:
            return m-1
        
        def get_valid(k):
            if k == 1:
                return {i for i in range(2, m+1) if Counter(bin(i)[2:])['1'] == 1}
            candi = get_valid(k-1)
            ans = set()
            for i in range(2, m+1):
                if Counter(bin(i)[2:])["1"] in candi:
                    ans.add(i)
            return ans
        
        def C(n, i):
            if i > n:
                return 0
            ans = 1
            i = min(i, n-i)
            for j in range(1, i+1):
                ans = ans * (n+1-j) // j
            return ans
        
        def get_count(n, cnt):
            if n == 0:
                return 1 if cnt == 0 else 0
            l = len(bin(n)[2:])
            if l < cnt:
                return 0
            if cnt == 0:
                return 1
            return C(l-1, cnt) + get_count(n-(1<<(l-1)), cnt-1)

        ans = 0
        ones = get_valid(k-1)
        print(ones)
        for cnt in ones:
            ans += get_count(n, cnt)
        return ans

提交代码评测得到:耗时41ms,占用内存18.14MB。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值