HDU 4588 Count The Carries (数学-- 二进制技巧枚举)

78 篇文章 0 订阅

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4588

大体题意:

给你两个十进制数字a和b ,求得二进制数从a 加加 一直加到b 总共二进制进位多少?

思路:

比赛时没有做出来,请教的同学:

不过思路的确很巧妙:

既然是求和,那么我们不妨先把这些数摆出来看看规律:

0   0000

1   0001

2   0010

3   0011

4   0100

5   0101

6   0110

7   0111

8   1000

9   1001

10 1010

11 1011

12 1100

13 1101

14 1110

15 1111

比如说我们想算1加到4 总共进了多少位,我们发现 1到4 第一列有2个1,那么这一列肯定进位2/2 = 1位,然后把这一位给第二列,这样第二列总共有3个1,这一列总共进3/2 = 1位,在把这1位传给第三列,为2/2 = 1位,这样总共进位1+1+1 = 3位。

因此我们只需要算a到b 每一列有多少个1即可!

那么问题又来了,n 最大是10亿瞎爆求解1的个数肯定不行。

其实每一列1的个数有规律的,第一列是2个一循环01  01   01, 第二列是四个一循环 0011 0011 0011.

这样规律就有了,我们可以先算0到b 多少个1,在算0到a-1多少个1,作差即可!

注意开大点 用long long 就行了!

详细见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100 + 10;
typedef long long ll;
ll ar[maxn],br[maxn],cr[maxn];
void getsum(ll n,ll *x){
    if (n <= 0)return;
    for (int i = 1; i <= 60; ++i){
        ll nn = n;
        x[i] += (nn >> i) * (1ll << (i-1));
        nn -= ((nn >> i) * (1ll << i)-1ll);
        if (nn > ( (1ll << i) >> 1ll) ) x[i] += nn - ( (1ll << i) >> 1ll );
    }
}
int main(){
    ll a,b;
    while(scanf("%I64d %I64d",&a,&b) == 2){
        memset(ar,0,sizeof ar); memset(br,0,sizeof br); memset(cr,0,sizeof cr);
        getsum(a-1,ar);
        getsum(b,br);
//        for (int i = 1; i <= 60; ++i)printf("%I64d\n",br[i]);
        for (int i = 1; i <= 60; ++i){
            cr[i] = br[i] - ar[i];
        }
        ll ans = 0ll;
        for (int i = 1; i <= 60; ++i){
            ans += cr[i]/2;
            cr[i+1] += cr[i]/2;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
/*
0 1000000000
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值