组合数学,对于长度为l的二进制数n,先求出长度小于l的满足意义的数,这里长度为l的数,都是指二进制以1开头的长度为为l的数,讨论以0开头的数在这种方法下没什么意义,因为每个数都可确定的知道其长度,并且是以1开头的,也就是说这样的分类可以包括所有的数并且没有重叠。然后再求长度为为l的小于n的满足要求的书。然后再检查n看他本身是否满足条件。这样就求出了1~n中满足条件的数的个数,还有c++提交除以2用位运算来实现,不然会wa
#include <iostream>
#include <cstdio>
using namespace std;
int f[33][33],re[33];
int getV(int x)
{
if(x<=1) return 0;
int a[33],l=0,sum=0,c=0;
while(x)
{
if(x%2==0) c++;
a[l++]=x%2;
x=x>>1;
}
int t=l/2;
if(l%2) t++;
if(c>=t) sum++;
int p;
int i,j;
for(i=2;i<=(l-1);i++) sum+=re[i];
for(i=l-2;i>=0;i--)
{
if(a[i])
{
for(j=t-1;j<=i;j++)
sum+=f[i][j];
}
else t--;
}
return sum;
}
int main()
{
int i,j;
f[0][0]=1;
for(i=1;i<=31;i++)
{
f[i][0]=1;
for(j=0;j<i;j++)
f[i][j+1]=f[i-1][j]+f[i-1][j+1];
if(i%2) re[i]=((1<<(i-1))-f[i-1][(i-1)/2])>>1;
else re[i]=(1<<(i-1))>>1;
}
int s,e;
while(~scanf("%d%d",&s,&e))
{
int v1=getV(e);
int v2=getV(s-1);
printf("%d\n",v1-v2);
}
return 0;
}