好久不写,这个题目又弄了几个小时,水平还是有限。
这道题我用组合数学来做,如果要求区间[a,b]的个数,先求出[2,a)的个数和[2,b]的个数,两者相减。
首先我们假设求 [1000,10000)(二进制表示)这个区间,那么在这个区间里高位都是为1的,剩下3位里面必须至少有两位为0。故为C(3,2)+C(3,3)=C(3,0)+C(3,1),因为左边和右边之和为2^3(二项式定理),故解为2^2。
那如果是[10000,100000),同理可以得到有C(4,2)+C(4,3)+C(4,4),因为 C(3,0)+C(3,1)+C(4,2)+C(4,3)+C(4,4)=2^4,所以前面那个式子的和为(2^4-C(4,2))/2.由上面可以分为奇偶情况来分别计算。
再假设要求[2,20)区间的个数,20=10111,那么我们先求出[10,10000)(二进制)这个就是上面讨论的情况了。然后再取为1的情况10011,右边第二位为1,如果为0,则有1000**,后面两位可以随意取,然后根据1/0的情况像上面一样讨论。然后再后移找为1的情况,直到完成。
代码:
#include<iostream> #include<cmath> using namespace std; int dp[35][35], sqr[35]; void Cn() //二的幂,组合 { int i,j; memset(dp,0,sizeof(dp)); dp[0][0]=1; for( i=1;i<=32;i++) for( j=0;j<=i;j++){ if(j==0) dp[i][j]=1; else dp[i][j]=dp[i-1][j]+dp[i-1][j-1]; } sqr[0]=1; for(i=1;i<=31;i++) sqr[i]=sqr[i-1]*2; } int Fn(int k,int d) // k个数,原来0比1多d个 { if(k-d<=0) return sqr[k]; int sum=0,i; if((k-d)%2) i=(k-d+1)/2; else i=(k-d)/2; for( ;i<=k;i++) sum+=dp[k][i]; return sum; } void Cal(int n,int&sum) { int temp,num[35],i,j; int one=0,zero=0; temp=n; for( i=0;temp;i++){ if( temp&1) num[i]=1; else num[i]=0; temp>>=1; } for( j=1;j<i-1;j++){ //i是二进制的长度。 if(j%2) sum+=sqr[j-1]; //奇偶的情况分析 else sum+=(sqr[j]-dp[j][j/2])/2; } one=0,zero=0; for( j=i-2;j>=0;j--){ if( num[j]){ sum+=Fn(j,zero-one); one++; } else zero++; } if(one+1<=zero) sum++; } int main() { int a,b,start,finish; Cn(); while( scanf("%d%d",&start,&finish)!=EOF){ a=b=0; Cal(start-1,a); Cal(finish,b); printf("%d\n",b-a); } return 0; }