NYOJ 222 整数中的1 (二进制位运算,找规律)

题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=222

整数中的1

时间限制: 3000 ms  |  内存限制: 1000 KB
难度: 4
描述
给出两个非负32位整型范围内的数a,b,请输出闭区间[a,b]内所有数二进制中各个位的1的总个数。
输入
一行,给出两个整形数a,b(0<=a<=b<=150000000),空格分隔。
输出
一行,输出结果
样例输入
1 2
样例输出
2

看起来是位运算,但是会超时。。所以要找一下规律。

对于一个数0到n的n+1个数,二进制第一位每2个数会出现1个1,第二位每4个数会出现2个连续的1,第三位每8个数会出现4个连续的1,第四位每16个数会出现8个连续的1。

这样规律就找到了。不过还要考虑一些特例,如果给的n不能正好被整除,多出的那部分只要超过区间的一半,那么超出的那部分一定也是1,因为每个区间中前一半是0,后一半是1。

对于while的结束条件也要好好考虑。

#include <iostream>
#include <cstdio>
using namespace std;
int getnum(int x) {
	if(x <= 0) return 0;
	int y = x + 1;   //因为该规律是从0000开始算起的,相当于一共有x+1个数 
	int q = 2, p = 1, sum = 0;  //每连续q个数出现p个连续的1 
	while(y >= q / 2) {		//位数不够才退出 
		sum += y / q * p;  //加整位 
		if(y % q > q / 2) sum += y % q - q / 2;  //不完整的需要判断是否大于区间的一半,因为每个区间前半部分是0,后半部分是1 
		q *= 2;
		p *= 2;
	}
	return sum;
}
int main() {
	int n, m;
	scanf("%d %d", &n, &m);
	printf("%d\n", getnum(m) - getnum(n - 1));
	return 0;
}



你可能感兴趣的:(NYOJ 222 整数中的1 (二进制位运算,找规律))