C - Count TheCarries

本文介绍了一种算法,用于计算两个给定数A和B之间所有连续整数进行二进制加法时所产生的进位总数。通过分析二进制数的特性,提出了一种高效的方法来解决该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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相加时的进位个数就可以。然后再累加入总数即可。

任意XY数相加的二进制进位个数为:X的二进制位个数+Y的二进制位个数-(X+Y)的二进制位个数。比如:

011010

010110

110000

原本X有3个1,Y有3个1,结果(X+Y)只有2个1了,只有在进位的时候会发生原本2个1变成1个1的情况(从而少了一个1)。所有少了几个1就表示进位了几次。

对于从AB的序列依次相加的二进制进位总数为:

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表示即可。

如何求0n的所有数的1sum表示?

0n的所有数的二进制和中,第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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值