题目链接:
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
*/