http://poj.org/problem?id=3252
本题 给出a,b
求【a,b】之间的所有round number
什么是round number? 就是一个数的二进制表示下,0的个数>=1的个数
这篇文章有详细的推导过程:http://www.cnblogs.com/lyy289065406/archive/2011/07/31/2122758.html
主体思路就是 把求区间【a,b】的RN数,转为求 小于k 的RN数的个数
然后只需要求 RN(b+1)-RN(a)即可得到题目答案
而求小于k的RN数,可分为两步: 设k的二进制有效位为len位
1:求长度小于len的RN数,长度小于len的数必然小于k
显然这部分好求,我们枚举每一个长度x,对该长度的RN数,便是 C【x-1】【y】,C【x-1】【y+1】....C【x-1】【x-1】,判断一下y的值即可
2:求长度等于len,值小于k的数
我们从次高位开始遍历,如果i位是1,那么当i位为0的时候,不管后面怎么变都肯定比k小,计算出当前第i位为0,i位以后任意排列的RN数(需要记录前面出现了几个0)
如果第i位为0,则不作处理,(因为后面会被计算上)
至于用到的组合数最大不超过31,打表法即可解决
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <set> #include <stack> #include <iostream> using namespace std; __int64 inf=15; double eps=0.000001; __int64 n,m; __int64 cc[100][100]; void pre() { __int64 i,j; for (i=0;i<=40;i++) cc[i][0]=1; for (i=1;i<=40;i++) for(j=1;j<=40;j++) cc[i][j]=(cc[i-1][j-1]+cc[i-1][j]); } __int64 C(__int64 i,__int64 j) { return cc[i][j]; } __int64 cal(__int64 x)//计算比x小的Round Num { __int64 i,j; __int64 tmp=x;__int64 cun=0; while(tmp) { tmp/=2;cun++; } __int64 sum=0; for (i=1;i<cun;i++) //计算长度小于len_X的RNum { for(j=(i-1)/2+1;j<i;j++) { sum+=C(i-1,j); } } //计算长度等于len_x的RN __int64 zero=0;//统计从高到低遇到的0的个数 for (i=cun-1;i>=1;i--) { __int64 tmp=x&(1<<(i-1)); if (tmp) //如果i位是1,那么当i位为0的时候,肯定比x小,计算出他们 { for(j=(cun+1)/2-zero-1;j<=i-1;j++) { if (j<0) continue;//不存在拥有当前指定零的个数的比x小的数存在 sum+=C(i-1,j); } } else { zero++; //如果i位为0,计数+1,比x小的数留给后面的算,本处不处理 } } return sum; } int main() { pre(); scanf("%I64d%I64d",&n,&m); __int64 t1=cal(n); __int64 t2=cal(m+1); printf("%I64d\n",t2-t1 ); return 0; }