题目链接:http://poj.org/problem?id=3252
题意:给定s、e,求[s,e]之间的Round Number的个数,RN数为二进制表示中0的个数大于1的个数的数,s、e<=2e9。
思路:计算[s,e]之间的RN数等价于getn(e+1)-getn(s),getn(s)表示[0,s)之间的RN数,注意RN数要求是正数,所以0不算,但其实算不算结果都一样。需要用到组合数,初始化组合数,用公式C(n,m)=C(n-1,m-1)+C(n-1,m)递推实现,用sum[i]表示长度为i的数中RN数的个数。用bit数组存储x的各个位的值,len表示其长度。则小于x的RN数分两种情况,一种长度小于len的,此情况的数恒小于x,但要注意的是数的第一位必须是1。长度小于len的情况比较简单,只需要把预处理的sum数组相加即可; 等于len的情况,从第二位开始讨论,一直到第len位,若为1,则通过组合数计算出这一位取0时有多少个RN数并加上,若为1,则跳过。
AC代码:
1 #include<cstdio> 2 using namespace std; 3 4 int s,e; 5 int C[35][35],sum[35]; 6 7 void init(){ 8 C[0][0]=1; 9 for(int i=1;i<=32;++i) 10 C[i][0]=C[i][i]=1; 11 for(int i=2;i<=32;++i) 12 for(int j=1;j<=i-1;++j) 13 C[i][j]=C[i-1][j-1]+C[i-1][j]; 14 for(int i=2;i<=32;++i) 15 for(int j=(i+1)>>1;j<=i-1;++j) 16 sum[i]+=C[i-1][j]; 17 } 18 19 int getn(int x){ 20 if(x<=1) return 0; 21 int ans=0,len=0,num0=0,num1=1,bit[35]; 22 while(x){ 23 bit[len++]=x%2; 24 x/=2; 25 } 26 for(int i=2;i<len;++i) 27 ans+=sum[i]; 28 for(int i=len-2;i>=0;--i) 29 if(bit[i]){ 30 for(int j=i;j>=0&&j+num0+1>=num1+i-j;--j) 31 ans+=C[i][j]; 32 ++num1; 33 } 34 else 35 ++num0; 36 return ans; 37 } 38 39 int main(){ 40 init(); 41 scanf("%d%d",&s,&e); 42 printf("%d\n",getn(e+1)-getn(s)); 43 return 0; 44 }