C - Count TheCarries
现在给你两个数A与B (0<=A<=B<=1000000000),然后要求A与A+1相加,结果再与A+2相加,直到加到B为止。不过过程中做的加法都是数的二进制表示形式的加法。求做的所有加法的二进制进位的总数。比如A+(A+1)共有8次二进制进位,(2A+1)+(A+2)有9次二进制进位,那么A到A+2一共有17次二进制进位。
输入:有最多100000个实例,每个实例用两个整数A,B表示,A=B=0时,表示输入结束。
输出:输出每个实例的进位总数。
分析:对于任意给出的实例A,B只要依次求出X与Y相加时的进位个数就可以。然后再累加入总数即可。
任意X与Y数相加的二进制进位个数为:X的二进制位个数+Y的二进制位个数-(X+Y)的二进制位个数。比如:
011010
010110
110000
原本X有3个1,Y有3个1,结果(X+Y)只有2个1了,只有在进位的时候会发生原本2个1变成1个1的情况(从而少了一个1)。所有少了几个1就表示进位了几次。
对于从A到B的序列依次相加的二进制进位总数为:
【A的二进制位数+(A+1)的二进制位数+…B的二进制位数】—【A+(A+1)+…+B的和的二进制位数】
对于每个实例A与B,只要依次计算A,A+1,。。。B的二进制中1的个数,再减去(A+A+1,+…+B)的二进制位个数即可。
以下代码超时:
#include<cstdio>
using namespace std;
int bitcount(long long x)
{
if(x==0)return 0;
returnbitcount(x/2)+(x&1);
}
int main()
{
long longa,b;
while(scanf("%lld%lld",&a,&b)==2&&a&&b)
{
longlong bit_sum=0,sum=0;
for(longlong i=a;i<=b;i++)
{
sum+=i;
bit_sum+=bitcount(i);
}
printf("%lld\n",bit_sum-bitcount(sum));
}
return 0;
}
解法二:现在想象从A到B所有的数都以二进制的形式叠加在一起相加:
1000111
1001000
1001001
1001010
Sum[64]=0000…4003122(一个数最多64位)
sum[64]保存数加的中间结果,sum[0]表示所有数在2^0的位上二进制的总个数,sum[i]表示所有数在2^i位上二进制的总个数。那么这些不同位上的1相加后进位个个数为:
2^0上的1相加产生的进位,加上 (2^1上的所有1 以及2^0上的1的进位 相加产生的进位),加上…
现在只要求出A到B序列所有数的1的sum表示即可。
如何求0到n的所有数的1的sum表示?
0到n的所有数的二进制和中,第2^i位上1的总数为:
n%(2^(i+1))-(2^i-1) > 0 时
[ n/(2^(i+1))]*(2^i)+n%(2^(i+1))-(2^i-1)
n%(2^(i+1))-(2^i-1) <= 0 时
[n/(2^(i+1))]*(2^i)
(自己分析一下2^0位,2^1位,2^2位看看有什么规律)计算一定要小心。
#include<cstdio>
using namespace std;
int get(int num[],long long n)//num[i]=j 表计算从0到n的数的二进制和(不进位) 中第2^i位的1的总个数为j个
{
for(int i=0;i<64;i++)num[i]=0;
int i;
for(i=0;i<64;i++)
{
if((1LL<<i)>n)break;
num[i]+= ( n/(1LL<<(i+1)) )*(1<<i) ;
if( n%(1LL<<(i+1)) > (1LL<<i)-1 ) num[i] += n%(1LL<<(i+1)) - ((1LL<<i)-1);
}
return i;
}
int main()
{
int a,b;
while(scanf("%d%d",&a,&b)==2&&a&&b)
{
int num1[64],num2[64];
get(num1,a-1);
get(num2,b);
for(int i=0;i<64;i++)num2[i]-=num1[i];
long long sum=0;
for(int i=0;i<64;i++)
{
sum +=num2[i]/2;
num2[i+1]+=num2[i]/2;
}
printf("%I64d\n",sum);
}
return 0;
}