题目链接: 题目链接
题意:如果一个数二进制n有k位1,那么f1[n] = k,如果k有s位二进制1,那么f2[n] = f1[k] = s. 如此往复,直到fx[n] = 1,此时的x就是n的”K值“,现在要求[L,R]内的”K值“为X的数有多少个。(1<=L<=R<=10^18)
解法:首先可以看到10^18最多只有61位左右的数,所以我们只需处理1~61之间每个数有多少个1,即可知道1~61之间每个数”K值“是多少。
然后就将求[L,R]之间的个数变成求[1,R]-[1,L-1],所以我们只需数出对于每个数n,[1,n]之间有多少个数的”K值“为X即可。
对于二进制来说,可以这样搜索出来:
比如<=101001,要满足有k个1的数的个数,那么我们从高位往低位扫,扫到第一个1,那么现在有两种情况:
1.此处放1:那么就等于求<=1001时放k-1个1的数的个数
2.此处放0:那么后面就随便放了,为C[5][k]
所以如此递归的搜索就可得出答案,也可以用DP做。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std; int Count(ll state) {
int cnt = ;
while(state) {
if(state & 1LL) cnt++;
state >>= ;
}
return cnt;
}
int WEI(ll state) {
int cnt = ;
while(state) {
cnt++;
state >>= ;
}
return cnt;
}
ll C[][];
int in[]; void init()
{
C[][] = ;
for(int i = ; i < ; i++) {
C[i][] = ;
for(int j = ; j <= i; j++) {
C[i][j] = C[i - ][j] + C[i - ][j - ];
}
}
memset(in,,sizeof(in));
in[] = ;
for(int i=;i<=;i++)
in[i] = in[Count(i)]+;
}
int X; ll get(ll state,int cnt) {
if(state < ) return ;
int len = WEI(state);
if(len < cnt) return ; // not enough
if(cnt == ) return ; // no demand
return get(state-(1LL<<(len-)),cnt-) + C[len-][cnt];
} ll getsum(ll R,ll L) {
ll ans = ;
for(int i=;i<=;i++)
if(in[i]+ == X) ans += get(R,i)-get(L-,i);
return ans;
} int main()
{
init();
int i,j;
ll L,R;
while(scanf("%lld%lld%d",&L,&R,&X)!=EOF && L+R+X)
{
ll ans = ;
if(X == && L == 1LL) { puts(""); continue; }
if(X == && L == 1LL) ans--; //1's binary code is 1, but 1 is not in (X==1)
ans += getsum(R,L);
cout<<ans<<endl;
}
return ;
}