主要分3种情况:
1.长度比n的2进制表示的长度要小,形如:1xxxxx的格式(首位=1)
2.n能不能满足
3.长度=n的2进制表示,但从某一位开始比n小
知识点:
利用杨辉三角预处理组合数
思想:
分类考虑
前缀和
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
ll C[40][40];
void init()//预处理出组合数
{
for(int i=0;i<=35;i++)
{
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
}
}
ll solve(int n)
{
if(n==0)
return 0;
int tn=n,len=0;
int bn[40]={0},sum0[40]={0};
while(tn)
{
bn[++len]=tn%2;
tn>>=1;
}
ll res=0;
for(int i=1;i<=len;i++)
sum0[i]=sum0[i-1]+(bn[i]==0);
for(int i=1;i<len;i++)//第一类1xxxxx(长度小于len)
{
for(int j=(i+1)/2;j<i;j++)
res+=C[i-1][j];
}
if(sum0[len]>=len-sum0[len])//第二类(n本身就满足)
res++;//cout<<"2:"<<res<<endl;
//第三种长度为len,但从某一位开始小于n,但不能是最高位,目的使长度不变的情况下,使之满足条件
int m0=(len+1)/2;//要使n为满足条件的数,应该保证含有的0的最小值
for(int i=1;i<len;i++)//<len:保证长度==len
{
if(bn[i]==1)//枚举除最高位以外的为1的位置,作为开始小于n的位置
{
int tmp=sum0[len]-sum0[i]+1;//原来共sum0[len]个0,要满足要求,整个式子0的个数>=m0,对于1~i的范围内,
//把i位置的1边成0后,还有i-1个位置可以自由取值,只要能够让1~i-1和i+1~n范围内的0的个数达到要求即可
int tol=i-1;//总的位置数
for(int j=max(0,m0-tmp);j<=tol;j++)//使0的个数达到要求所需要的最少的0的个数~位置数目
res+=C[tol][j];//cout<<"res="<<res<<endl;
}
}//cout<<"res="<<res<<endl;
return res;
}
int main()
{
int s,f;
init();
while(scanf("%d%d",&s,&f)!=EOF)
{
ll ans=solve(f)-solve(s-1);
printf("%lld\n",ans);
}
return 0;
}