题目让求从a加到b的二进制进位次数之和。题目给的是从a到b每次进行两个数的加法,然后求和。
自己YY一下就知道可以从a到b一起做加法,求进位次数和。
具体做法是我们先统计出来从a到b二进制的各位上有多少个1,假如二进制某位有x个1,那么该位进位次数即为x/2,然后给下一位进位x/2。然后把二进制位数扫一遍就行了。
现在的麻烦是怎么统计a到b的二进制的各位上有几个1。通过找规律我们知道:对于二进制的第一位,每两个数中会有1个1。对于二进制的第二位,每四个数中会有2个1。对于二进制的第三位,每八个数中会有4个1,,,对于二进制的第i位,每2^i个数中会有2^(i-1)个1。
于是我们就可以先统计从1到a-1有多少个1,再统计从1到b有多少个1,然后计算出从a到b有多少个1就OK了。具体看cal函数的实现
#pragma warning(disable:4996)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 34;
int num1[N], num2[N], num3[N];
void cal(int a, int *num){
//先初始化为0
for (int i = 1; i <= 32; i++)num[i] = 0;
if (a == 0)return;
//枚举二进制各个位
for (int i = 1; i <= 32; i++){
int T = 1 << i;//二进制第i为的周期为T
int have = T / 2;//每个周期里含有have个1
num[i] += (a / T*have);
//对于不完全的周期,单独统计下
int left = a%T;
//找找规律很容易发现这个公式的
have = left - (1 << (i - 1)) + 1;
if (have > 0)num[i] += have;
}
}
int main(){
int a, b;
while (~scanf("%d %d", &a, &b)){
cal(a - 1, num1);
cal(b, num2);
int tmp = 0;
for (int i = 1; i <= 32; i++){
num3[i] = num2[i] - num1[i];
}
LL ans = 0;
for (int i = 1; i <= 32; i++){
ans += num3[i] / 2;
num3[i + 1] += num3[i] / 2;
}
while (num3[33]){
ans += num3[33] / 2;
num3[33] = num3[33] / 2;
}
printf("%lld\n", ans);
}
return 0;
}