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 k−1次操作变为 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 p−1个位置上任意放置 k k k个 1 1 1即可;
- 最后,如果最大位上取 1 1 1,那么问题就退化为了在不大于 n − 2 p − 1 n-2^{p-1} n−2p−1的数当中一共有多少个数其二进制位上的 1 1 1的个数恰好为 k − 1 k-1 k−1个。
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。