题目链接:poj 3252
找出给定范围内,二进制0大于1的数的数量
组合数学,结果为sum(r)-sum(l-1),求sum通过组合数找出小于当前数长度的所有round数,对于当前长度遇1置0后统计之后的所有数,最后特判一下本身即可。
/*********************************************************************
FileName: 3252.cpp
Author: kojimai
Created Time: 2014年08月06日 星期三 15时22分54秒
*********************************************************************/
//找出给定范围内二进制数0数目大于1的有多少个
/*
sum(i)表示从1到i有多少个round数,结果即为sum(r)-sum(l-1)
求sum(i):
首先统计所有二进制长度比i小的round数
然后对找出i的二进制数,从高位到低位扫一遍遇到1则该位置0统计后面剩余可能的round数
最后判断一下这个数本身是否是round数即可
*/
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int C[33][33];
void init()
{
C[0][0]=1;
for(int i=1;i<=31;i++)
{
C[0][i]=C[i][i]=1;
}
for(int i=2;i<=31;i++)
{
for(int j=1;j<i;j++)
{
C[j][i]=C[j][i-1]+C[j-1][i-1];
}
}
return;
}
int a[33];
int turn(int num)//转化为二进制数
{
int now=0;
while(num)
{
a[now]=num%2;
now++;
num/=2;
}
return now;
}
int Cnt(int len,int l1,int l2)//总长度为len,已经有l1个1,l2个0
{
int ret=0;
for(int i=0;i+l1<=len/2;i++)
ret+=C[i][len-l1-l2];
return ret;
}
int solve(int num)
{
int ans=0,cnt1=1,cnt0=0;
int len=turn(num);
for(int i=2;i<=len-1;i++)//找出二进制长度比num小的二进制数
{
for(int j=1;j<=(i/2);j++)//利用组合数求出1的数目从1到i/2的所有的情况
ans+=C[j-1][i-1];
}
//cout<<" ans="<<ans<<endl;
for(int i=len-2;i>=0;i--)
{
if(a[i]==1)
{
ans+=Cnt(len,cnt1,cnt0+1);//若当前位为1,则该位置0之后统计之后的所有数
cnt1++;
//cout<<" ans="<<ans<<endl;
}
else
cnt0++;
}
if(cnt1<=cnt0)//判断这个数本身
ans++;
return ans;
}
int main()
{
init();
int start,end;
scanf("%d%d",&start,&end);
cout<<solve(end)-solve(start-1)<<endl;
return 0;
}