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;
}