如果一个数的二进制表示里面,0的个数大于等于1 的个数(不计算前导0),那么这个叫做round number。
求一个给定区间内的round number。
思路:
假设一个二进制数的第 i 位(从右往左数,从1 开始)是1 的话,那么当第i位为0的时候,从这个数的第 i 位往右的那些二进制位可以是任何数(1或者0 ),换句话说,就是第 i 位右边的 二进制位不论是1还是0都小于原来的数字的,于是用一下组合数学就可以求解了。
假设第 i 位的左边没有出现1,那么第 i 位左边的0 就可以忽略不计,如果出现了1 ,那么第i位左边的0就要计算在内,然而,只有最后一个二进制位(从右往左)的左边没有1,所以只需要对最后一位特殊处理一下。
接下来论靠直接 debug 的重要性,由于 数字0 实在是太TM 的特殊了,我的代码里用一个函数计算所有小于n 的round number ,但是当n等于 1的时候,这个函数返回0,正确答案是1,我想这应该是一个唯一的情况,所以对1进行了特殊判断(见solve函数),于是还真就只是这么一个特殊情况。
更详细的细节参见代码
#include<iostream>
#include<cstring>
#include<cstdio>
#define bug
using namespace std;
const int MAXN = 40;
int num[MAXN],len;
int c[MAXN][MAXN];
int dp[MAXN];
//dp[i]表示长度为i的所有以1开头的所有的二进制数的round number 个数
void init_c()
{
for(int i=0;i<MAXN;i++)c[i][0]=1;
for(int i=1;i<MAXN;i++)
{
for(int j=1;j<MAXN;j++)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
dp[1]=1;
for(int i=2;i<MAXN;i++)
{
dp[i]=0;
int tar = (i-1)/2+1;
for(int j=tar;j<=i-1;j++)
{
dp[i] += c[i-1][j];
}
}
#ifdef bug2
for(int i=1;i<=33;i++)
printf("dp[%d]->%d ",i,dp[i]);
#endif
}
int solve(int n)
{
if(n==1)return 1;
len = 1;
int temp = n;
while(n)
{
num[len++]=n%2;
n/=2;
}
len--;
int nOne = 1;
int nZero=0,stdZero=0,tarZero;
stdZero = len/2+(len&1);
int res = 0;
for(int i=1;i<len;i++)res+=dp[i];
for(int i=len-1;i;i--)
{
if(num[i])
{
tarZero = stdZero - nZero - nOne;
for(int j=tarZero;j<i;j++)
{
res += c[i-1][j];
}
}
else
{
nZero++;
}
}
#ifdef bug3
printf("n=%d res=%d\n",temp,res);
#endif
return res;
}
int main()
{
freopen("data.in","r",stdin);
init_c();
int a,b;
while(~scanf("%d%d",&a,&b))
{
printf("%d\n",solve(b+1)-solve(a));
}
return 0;
}