Round Numbers 就是一个表示成二进制的时候0比1多或者相等的正数,注意是正数,所以0就肯定不是了。
题目是给定一个区间,问在这个区间上的Round Numbers有多少个?
首先是求出小于等于n的Round Numbers有多少个。
我先举个例子来先说明,再来说一般方法。
比如: 22 = 10110b 如果要求 <=22的Round Numbers,也就是找出1-22有多少个二进制的0不少于1的数的个数。
22的二进制长度是5.
首先找长度比5小的Round Numbers(长度比5小的数肯定小于22啦)
长度为4的话,第一位必须是1,后面三位的话,可以有2个0,3个0
所以就是C(3,2)+C(3,3);
长度为3的Round Numbers,同理有 C(2,2);//注意不要把第一位1忘记了
长度为2的Round Numbers,有 C(1,1)
长度为1的Round Numbers,有 0个
下面是找长度和22相同的Round Numbers。
首先第一位是1.
22的第二位是0,所以第二位不能为1,必须是0
第三位为0的话,(前面有了2个0,1个1),后面两位可以有1个0,2个0
C(2,1)+C(2,2)
接下来把第三位恢复为1,看第四位。假如第四位是0,(前面有2个0,2个1),后面一位必须是0 C(1,1)
所以大致求的过程就如上面所述。
首先先推个公式,就是长度为len的Round Numbers的个数。
长度为len,第一位肯定是1了。
那么后面剩下 len-1位。
如果len-1是偶数。
那么 C(len-1,(len-1)/2+1)+C(len-1,(len-1)/2+2)+````C(len-1,len-1)
= ( 2^(len-1)-C(len-1,(len-1)/2) )/2;
如果len是奇数
那么就是 ( 2^(len-1) )/2
所以上面求比N长度小的Round Numbers是很好求的了。
至于求长度的,则是逐渐把每一位1变为0,去求后面的,就可以保证比n小了。
#include<stdio.h>
#include<iostream>
using namespace std;
int C[33][33];
void init()
{
C[0][0]=1;
C[1][0]=1;C[1][1]=1;
for(int i=2;i<33;i++)
{
C[i][0]=1;
for(int j=1;j<i;j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
C[i][i]=1;
}
}
int bits[33];
int calc(int n)//求小于等于n的 Round Numbers
{
if(n<=1)return 0;//这个条件必须加
int len=0;
while(n>0)
{
if(n&1)bits[len++]=1;
else bits[len++]=0;
n>>=1;
}
int ans=0;
for(int i=len-1;i>0;i--)
{
if(i%2==0)ans+=((1<<(i-1)))/2;
else ans+=((1<<(i-1))-C[i-1][(i-1)/2])/2;
}
int cnt0=0,cnt1=0;
for(int i=0;i<len;i++)
{
if(bits[i]==0)cnt0++;
else cnt1++;
}
if(cnt0>=cnt1) ans++;//n本身是 Round Number
cnt0=0;
cnt1=1;
for(int i=len-2;i>=0;i--)
{
if(bits[i]==1)//后面有i位,第i位当成0
{
for(int j=i;j>=0&&j+cnt0+1>=i-j+cnt1;j--)ans+=C[i][j];
cnt1++;
}
else cnt0++;
}
return ans;
}
int main()
{
init();
int a,b;
while(scanf("%d%d",&a,&b)!=EOF)
{
printf("%d\n",calc(b)-calc(a-1));
}
return 0;
}