组合数学,对于长度为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; }