标签:
本题的误区是用位运算法,位运算法基数太大暴力枚举肯定超时。。。
#include <iostream>
using namespace std;
int Count(int n){
int ans = 0;
while(n){
ans += n & 1; // n 与 1,判断第一位是0还是1
n >>= 1; // 向右移1位 ,相当于 n /= 2
}
return ans;
}
int main()
{
int start, end;
long long ans;
while(scanf("%d%d", &start, &end) != EOF){
ans = 0;
for(int i = start; i <= end; i ++){
ans += Count(i);
}
printf("%d\n", ans);
}
return 0;
}
肯定会超时,所以本题不适合用位运算法,本题是一个找规律题。
首先观察0-16用二进制表示:
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
会发现:
第一位的1,每隔2就出现。第二位的1,每隔4出现2次。第三位的1,每隔8出现4次。第四位的1,每隔16出现8次。
所以,可以判断[0-a]中1的个数为:第一位 (a + 1) / 2 ,第二位 (a + 1) / 4 如果(a + 1 ) % 4 > 2 ,则还需要加这部分。
#include <stdio.h>
int gogo(int x)
{
int y;
int p = 2, q = 1, sum = 0,temp;
y = x + 1; //因为从0000,第二个0001开始计算所以应该加以,把第一个全为零的数也加上
if (x <= 0)
return 0;
while (x >= p / 2) //当这一次剩下的大约上一次循环的就有没有加到的,所以继续循环
{
sum += y / p*q; //判断这是第几次有几个1
if (temp = (y%p - p / 2)) //如果余下的大于上一次循环的,就再加上因为这一位中在以前是没有加到过的。
sum += temp;
p *= 2;
q *= 2;
}
return sum;
}
int main()
{
int n, m;
while (~scanf("%d%d", &n, &m))
{
printf("%d\n",gogo(m)-gogo(n-1)); //区间是闭区间,所以包括n要减去n-1;
}
return 0;
}
标签:
原文地址:http://www.cnblogs.com/luguo/p/4346043.html