您的位置:首页 > 其它

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

2016-07-28 11:12 295 查看
题目链接:

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
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: