SOJ 3598 Binary

本文介绍了一种高效计算从0到n的所有整数在二进制表示中1的数量的方法。通过分析二进制数的规律,提出了一种非暴力的算法实现,并提供了完整的C++代码示例。

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

题目连接:http://zuojie.3322.org:88/soj/problem.action?id=3598

Description

For the sequence 0, 1, 2, . . , n, you are required to caculate the number of 
'1's when we write them in binary form.

Input

Multiple test cases, please process till EOF.
For each test case, there is only one line containing only one integer n
(0<=n<2^31).

Output

For each test case, output the answer in a single line.

Sample Input

5
1164
20071010
20100512
835672545

Sample Output

Case 1: 7
Case 2: 5744
Case 3: 239720074
Case 4: 240071643
Case 5: 12245574353

解题思路:

首先观察题目的数据量:是2^31,所以直接暴力+位运算肯定是不可行的。

那么我们可以观察一下二进制数的规律:

00000

00001

00010

00011

00100

00101

00110

00111

01000

01001

01010

01011

01100

01101

01110

01111

10000

以上是0-16的十进制数的二进制码,我们可以很容易发现,最后一位为1的个数是n的一半

第二位如果足够长的话也应该是一半

具体地说:最后一位一次出现0一次出现1,第二位两次出现0两次出现1,第三位四次出现0再四次出现1。

于是可以归纳一下,第n位就有:先出现2^(n-1)次0,再出现2^(n-1)次1。循环节是2^n

于是剩下的工作就非常的简单了

PS:注意到,n比较大,所以计算出来的结果会超过int,所以要用long long,另外SOJ不支持__int64这种数据类型。。

我的代码:
#include<stdio.h> #include<iostream> using namespace std; long long POW(long long a,long long n) { long long i,ret=1; for(i=1;i<=n;i++) ret=ret*a; return ret; } int main() { long long n,i,t=1,ans,s,a,b; while(cin>>n) { ans=0; ans=ans+(n+1)/2; for(i=2;i<=31;i++) { s=n-(POW(2,i-1)-1); if(s<0) break; a=POW(2,i); b=a/2; ans=ans+(s/a*b); if(s%a>b) ans=ans+b; else ans=ans+s%a; } cout<<"Case "<<t<<": "<<ans<<endl; t++; } return 0; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值