![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLzcmaOpXQ61EeVpHW3BjMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL2EzM4ATOzATMyEjNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
思路:分為最高位為0和1計算。注意:n的二進制位位數為 m = [ l o g 2 n ] + 1 m=[log_2 n]+1 m=[log2n]+1。
最高位為0時,後面的各二進制位可以任意取0或1,共 C m k C_m^k Cmk種情況。
最高位為1時,這個我還真沒想出來,隻能暴力求解。可以減少求解次數,比如k=2,m=10,可以從513(1000000001)求解到min(n,768(1100000000))。
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long int64;
int k=10,bin[64];
int64 combin(int t,int b=k) {
if(t==k+1) return 1;
if(b>t/2) b=t-b; //加一句
int64 fz=1,fm=1;
for(int64 i=t;i>t-b;i--)
fz*=i;
for(int64 i=b;i>1;i--)
fm*=i;
return fz/fm;
}
void add(int t,int &last) {
for(int i=t-1;i>=0;i--) {
if(bin[i]==0) {
bin[i]=1;
last++;
return;
}
else if(bin[i]==1){
bin[i]=0;
last--;
}
}
}
int64 minnum(int wei) {
int64 res=0;
for(int i=0;i<k-1;i++) {
res+=(int64)pow(2,i);
}
res+=pow(2,wei-1);
return res;
}
int64 maxnum(int wei) {
int64 res=0;
for(int i=wei-1;i>=wei-k;i--) {
res+=(int64)pow(2,i);
}
return res;
}
int main(){
int64 n,t,res=0;
int one=0;
scanf("%I64d%d",&n,&k);
for(t=0;(int64)pow(2,t)<=n;t++);
res=combin((int)(t-1)); //最高位非0的情況
//最小數為100....11(共t位,有k個1,除最高位外的k-1個1均在最低位連續排列)
//最大為111....00(共t位前k個為1)
int64 minn=minnum(t),maxn=maxnum(t),tmp=minn;
//預處理最小數
for(int i=0;i<t;i++) {
bin[t-i-1]=tmp%2;
if(bin[t-i-1]==1) one++;
tmp/=2;
}
if(one==k) res++;
//不用每個數都轉二進制
//時間複雜度與n轉換為二進制後與11....000(共t位,前k個為1)的差有關,與n大小關系較小
for(int64 j=minn+1;j<=maxn&&j<=n;j++) {
add(t,one);
if(one==k) res++;
}
printf("%I64d",res);
return 0;
}