以sample为例子
[2,12]区间的RoundNumbers(简称RN)个数:Rn[2,12]=Rn[0,12]-Rn[0,1]
即:Rn[start,finish]=Rn[0,finish]-Rn[0,start-1]
所以关键是给定一个X,求出Rn[0,X]
现在假设X=10100100
这个X的二进制总共是8位,任何一个小于8位的二进制都小于X
第一部分,求出长度为[0,7]区间内的二进制是RoundNumber的个数
对于一个长度为Len的二进制(最高位为1),如何求出他的RoundNumbers呢(假设为用R(len)来表达),分为奇数和偶数两种情况
1、奇数情况:在Len=2k+1的情况下,最高位为1,剩下2k位,至少需要k+1为0
用C(m,n)表示排列组合数:从m个位置选出n个位置的方法
R(len)=C(2k,k+1)+C(2k,k+2)+...+C(2k,2k).
由于 A:C(2k,0)+C(2k,1)+...+C(2k,2k)=2^(2k)
B:C(2k,0)=C(2k,2k), C(2k,1)=C(2k,2k-1) ,,C(2k,i)=C(2k,2k-i)
于是 C(2k,0)+C(2k,1)+...+C(2k,2k)
= C(2k,0)+C(2k,1)+...+C(2k,k)+C(2k,k+1)+C(2k,K+2)+...+C(2k,2k)
= 2*R(len)+C(2k,k)
=2^(2k)
所以R(len)=1/2*{2^(2k)-C(2k,k)};
2. 偶数情况 len=2*k,类似可以推到 R(len)=1/2*(2^(2k-1));
第二部分,对于上面这个长度为8的例子:即X=10100100,首先如果本身是RoundNumbers,第二部分的结果总数+1
第一部分已经将长度小于8的部分求出。现在要求长度=8的RoundNumber数目
长度为8,所以第一个1不可改变
现在到第二个1,如果Y是前缀如100*****的二进制,这个前缀下,后面取0和1必然小于X,已经有2个0,一个1,剩下的5个数字中至少需要2个0,
所以把第二个1改为0:可以有C(5,2)+C(5,3)+C(5,4)+C(5,5)
现在第三个1,也就是前最为101000**,同样求出,至少需要0个0就可,所以有C(2,0)+C(2,1)+C(2,2)个RoundNumbers
。。。
将所有除了第一个1以外的1全部变为0,如上算出有多少个RoundNumbers,结果相加(由于前缀不一样,所以后面不管怎么组合都是唯一的)
将第一部分和第二部分的结果相加,就是最后的结果了。
精度要求方面,用int就可以了:two billion=20亿<2*1024*1024*1024=2^31,需用31位来表示数组,由于第一位总是1,所以求组合数的时候最多求30,C(30,k),k取值区间是[0,30],因为C(k,i)<2^k,所以结果用int表示就可以
本文还是开成__int64位。
#include <stdio.h>
#include <iostream>
#include <cmath>
using namespace std;
int Arr[32];
bool flag[32],isOK;
//计算组合m组中的n组
__int64 C(int m,int n)
{
__int64 r=1;
int h=(m-n)<n?(m-n):n;
for(int i=1;i<=h;i++)
{
r*=m;
r/=i;
m--;
}
return r;
}
/*
make_list(){
for(int i=0;i<33;i++){
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
*/
int Len(int n)
{
return (int)(log(n*1.0)/log(2.0))+1;
}
int make_list(){
Arr[1]=0;
for(int i=2;i<=31;i++)
for(int k=(i-1)/2+1;k<=i-1;k++)
Arr[i]+=C(i-1,k);
return 0;
}
__int64 math(int n){
if(n==0)
return 0;
int zero=0,one=1,rightzero,len=Len(n);
__int64 cnt=0;
memset(flag,0,sizeof(flag));
//累加Len(n)之前的和
for(int i=1;i<len;i++)
cnt+=Arr[i];
//确定n的那些位为1
for(int i=0;i<len;i++)
{
if((n>>i) & 1)
flag[i+1]=true;
else
flag[i+1]=false;
}
for(int i=len-1;i>0;i--)
{
//n为偶数
if(len%2==0)
{
rightzero=len/2;
if(flag[i])
{
++one;
for(int k=rightzero-zero-1;k<i;k++)
{
if(k>=0)
cnt+=C(i-1,k);
}
}
else
++zero;
}
//n为奇数
else
{
rightzero=(len+1)/2;
if(flag[i])
{
++one;
for(int k=rightzero-zero-1;k<i;k++)
{
if(k>=0)
cnt+=C(i-1,k);
}
}
else
++zero;
}
}
zero=0;one=0;
//判断n自身是不是Round Numbers
for(int i=1;i<=len;i++)
{
if(flag[i])
one++;
else
zero++;
}
if(zero>=one)
cnt++;
return cnt;
}
int main(){
int sta, fin;
make_list();
cin >> sta >> fin;
cout << math(fin) - math(sta - 1) << endl;// 这里记得是sta- 1。
system("PAUSE");
return 0;
}