题目连接: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. |
InputMultiple test cases, please process till EOF. For each test case, there is only one line containing only one integer n (0<=n<2^31). |
OutputFor each test case, output the answer in a single line. |
Sample Input5 1164 20071010 20100512 835672545 |
Sample OutputCase 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; }