- 描述
- 给出两个非负32位整型范围内的数a,b,请输出闭区间[a,b]内所有数二进制中各个位的1的总个数。
- 输入
- 有多组测试数据
每组测试数据只有一行,是两个整型数a,b(0<=a<=b<=150000000),空格分隔。
当a,b都是0时表示输入结束,该组输入不用计算。
测试数据的组数不超过1000组 输出 - 对于每组输入,输出区间[a,b]内所有数二进制中各个位的1的总个数。 样例输入
-
1 2 100 200 0 0
样例输出 -
2 419
- 有多组测试数据
本来想到要出一道求1-n中每个数的二进制为1的个数,当n小时,按普通方法就可以了,
当n比较大就需要找规律,将1-15列出来就会发现规律了
1
10
11
100
101
110
111
1000
1001
1010
1011
1100
1101
1110
1111
当二进制位为4位的时候前面2^3个1已经确定了,后面3位就是前面1-3位二进制的和吗?
所以就可以直接预处理就结果算出来,查询的时候只需要算一段就可以了,然后那
一段就可以通过每次去掉前面那个1得到。
#include <stdio.h>
#define LL long long
const int maxn = 55;
LL cnt[maxn], sum[maxn], bit[maxn];
void print ( LL a[] )
{
for ( int i = 0; i < maxn; i ++ )
printf ( "%lld ", a[i] );
printf ( "\n" );
}
//发现位数是按1 2 4 8....增长
LL get_ans ( LL n )
{
LL ans = 0, t;
int begin;
while ( n > 0 )
{
begin = 0;
t = n;
while ( t >= ( 1 << begin ) )
{
t = t-( 1 << begin );
ans = ans+cnt[begin];
begin ++;
}
if ( t == 0 ) //有可能是2^k-1,那么t就会为0就不需要算了
break ;
ans = ans+t;
n = n & bit[begin]; //去掉前面那个1
}
return ans;
}
int main ( )
{
LL n, m;
sum[0] = cnt[0] = 1;
for ( int i = 1; i < maxn; i ++ )
{
cnt[i] = ( 1 << i )+sum[i-1]; //将二进制位为i的个数求出来
sum[i] = sum[i-1]+cnt[i]; //去掉前面一个1就变成i-1,变化即为前i-1的和
bit[i] = ( 1 << i )-1; //2^i-1取出最后i位
}
while ( ~ scanf ( "%lld%lld", &n, &m ), n || m )
{
if ( n == 0 ) //考虑n为0
n = 1;
printf ( "%lld\n", get_ans ( m )-get_ans ( n-1 ) );
//相减就可以了
}
return 0;
}