题目描述
小蓝最近在学习二进制。他想知道 1 到 N 中有多少个数满足其二进制表示中恰好有 K 个 1。你能帮助他吗?
输入描述
输入一行包含两个整数 N 和 K。
输出描述
输出一个整数表示答案。
输入输出样例
示例
输入
7 2
输出
3
评测用例规模与约定
对于 30% 的评测用例,1≤N≤1e6,1≤K≤10。
对于 60% 的评测用例,1≤N≤2×1e9,1≤K≤30。
对于所有评测用例,1≤N≤1e18,1≤K≤50。
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
思路:
首先我们肯定要先把n转为二进制
nums=[]
while n:#
nums.append(n%2)
n//=2
我们可以想到,假如将n转为二进制后有m位,那么此时是不是使用组合就可以了?|
例如:m=5,k=2。那么答案会是C(5,2)=10吗?
其实不是的,比如n=17,转二进制为【10001】是五位,但是对于【11000】【10100】....之类的
虽然也是五位,但是其大小已经超过了n!!!,注意我们求的是1~n
此时我们就要分情况讨论了,我们可以从高向低位看,若当前位为1,有两种情况:
1.填0,那么就要在后面找够剩下的1,此时就可以使用组合了,因为当前位本是1,现在填了0,那么后面无论怎么填都不会大于n
例如:n转二进制为【11001】,若当前遍历到下标1也就是第二个1,那么如果将当前填0,后面的两个1无论怎么填都不为过,比如【10101】【10011】...之类的
2.填1,那么此时剩余的1就少了一个,因为我们用了一个1
其实学过数位dp的朋友就能知道这是一个数位dp题。
代码实现:
f=[[0]*(62) for i in range(62)]#[位数][1数]:方案数量
for i in range(62):
f[i][0]=1
f[i][i]=1
for j in range(1,i):
f[i][j]=f[i-1][j]+f[i-1][j-1]
def dp(n,k):#求1到n有几个满足有k个1
if n==0:
return 0
n1=n
nums=[]
while n:#拆分
nums.append(n%2)
n//=2
if len(nums)<k:#不够k位
return 0
if len(nums)==k:
return 1 if n1==((2**k)-1) else 0#n转为二进制整好k个1?
last=0#出现了几个1
ans=0#答案
for i in range(len(nums)-1,-1,-1):#高到低
x=nums[i]
if x:#为1
ans+=f[i][k-last]#i位实际是低一位,若填0,剩下的1在后面补齐
last+=1#发现一个1
if i==0 and last==k:#其本身也有可能符合条件
ans+=1
return ans
n,k=map(int,input().split())
print(dp(n,k))