小易是一个数论爱好者,并且对于一个数的奇数约数十分感兴趣。一天小易遇到这样一个问题: 定义函数f(x)为x最大的奇数约数,x为正整数。 例如:f(44) = 11.
现在给出一个N,需要求出 f(1) + f(2) + f(3).......f(N)
例如: N = 7
f(1) + f(2) + f(3) + f(4) + f(5) + f(6) + f(7) = 1 + 1 + 3 + 1 + 5 + 3 + 7 = 21
小易计算这个问题遇到了困难,需要你来设计一个算法帮助他。
输入一个整数N (1 ≤ N ≤ 1000000000)
输出一个整数,即为f(1) + f(2) + f(3).......f(N)
7
21
```
# 来个python的递归实现,数据很大要用Decimal处理大数字
# 前提:奇数的最大奇约束就是他自己,偶数的最大奇约束就是一直除以2直到除成奇数
# 分奇数偶数,当n为偶数时,n以下的奇数全部加起来,即n^2/4, 剩下的偶数都除以二就变成
# 了1,2,3,...,n/2这就可以递归了。当n为奇数是也是类似的
from decimal import *
n = int(input())
def digui(n):
if n == 1:
return 1
if n % 2 == 0:
return Decimal(n**2)/Decimal(4) + digui(Decimal(n/2))
else:
return Decimal((n+1)**2)/Decimal(4) + digui(Decimal((n-1)/2))
print(digui(n))
```
#include <iostream>
using namespace std;
long long sum(long long N){
if(N==1)return 1;
long long res=0;
if(N%2==0){
res+=N*N/4;
res+=sum(N/2);
}else{
res+=(N+1)*(N+1)/4;
res+=sum((N-1)/2);
}
return res;
}
int main(){
long long N;
while(cin>>N){
cout<<sum(N)<<endl;
}
return 0;
}
#include <stdio.h> long bigOddSum(long num, long sum) { if (num == 0) { return 0; } if (num % 2) { sum += (num / 2 + 1) * (num / 2 + 1); sum += bigOddSum((num-1)/2, 0); } else { sum += (num / 2) * (num / 2); sum += bigOddSum((num)/2, 0); } return sum; } int main() { long num; long sum = 0; scanf("%ld", &num); sum = bigOddSum(num, 0); printf("%ld\n", sum); return 0; }
#include <iostream> #include <vector> using namespace std; int main(){ int N; while (cin >> N){ long long sum = 0; /* * n 为偶数(奇数时情况区别不大) * f(1) + f(2) + ... + f(N) = f(2) + f(4) + ... + f(N) + f(1) + f(3) + ... + f(N-1) * f(1) + f(3) + ... + f(N-1) = 1 + 3 + ... + N-1 = (N)*(N)/4; * f(2) + f(4) + ... + f(N) = f(1) + f(2) + ... + f(N/2); !!! Important here. */ while (N){ if((N&0x1)==1){ sum += (long long)(N+1)*(N+1)/4; // 防止溢出 N = (N-1)/2; } else{ sum += (long long)(N)*(N)/4; N = N/2; } } cout << sum << endl; } return 0; }
/*思路是:如果n是偶数,采用折半的方式,如果为奇数,首先将最后一个奇数加到结果中,然后再求前面n-1个偶数的结果*/ #include<iostream> using namespace std; int main(){ long n; while(cin>>n){ long long sum=0; while(n){ if(n%2==1){//如果是奇数个元素,将最后一个奇数加到结果中 sum+=n; --n;//然后求前面n-1个偶数的结果 } else{ sum+=(1+n-1)/2*n/2;//如果是偶数个元素,采用折半的方式,后面n个元素一次为1,3,5,7,。。。n-1 n/=2; } } cout<<sum<<endl; } return 0; }
import java.util.Scanner; public class Main{ public static void main(String[] args) { Scanner s=new Scanner(System.in); long num=s.nextInt(); long sum=0; for(long i=num;i>0;i=i/2){ long temp=(i+1)/2; sum+=temp*temp; } System.out.println(sum); } } 总体思路: 因为奇数的最大奇数约数就是自己啊,对于偶数我们只能一直除2直到得到一个奇数即为最大奇数约数 比如1 2 3 4 5 6 7 8 9 10 即n=10 ,此时奇数有1 3 5 7 9 我们把这几个奇数相加然后n/2 得到第二轮序列序列 1 2 3 4 5 分别对应上次的2 4 6 8 10 五个偶数,这是我们再加1 3 5 依次类推 细节问题: 当n为偶数,就有n/2个奇数,根据等差数列求和公式 即((首项+末项)*项数)/2,我们知道n/2个奇数和为((1+n-1)*n/2)/2, 即为(n/2) * (n/2),此时n为偶数,因此 (n/2) * (n/2) = ((n+1)/2) * ((n+1)/2) 当n为奇数,有(n+1)/2个奇数,此时奇数和为((n+1)/2) * ((n+1)/2) 因此两种情况可以用一个等式来总结
//找规律,你会发现基数都是直接取,偶数/2,得到的基础直接取,偶数继续/2,每次循环,可以把规模缩小1/2。 //要注意的是((i + 1) / 2) * ((i + 1) / 2)运算时候,括号的优先级。 #include "iostream" using namespace std; typedef long long LL; int main() { LL N; LL Ans = 0; while (cin >> N) { Ans = 0; for (LL i = N; i > 0; i = i / 2) Ans += ((i + 1) / 2) * ((i + 1) / 2); cout << Ans << endl; } return 0; }
package com.suda.alex; import java.util.Scanner; public class Test6 { public static void main(String[] args) { // TODO Auto-generated method stub Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int n = scanner.nextInt(); System.out.println(sumOfMaxOdd(n)); } } /* * 奇数的最大约数就是本身。问题就是求所有f(i), i为偶数的和 因为要求的是最大奇约数,所以f(2k) = f(k),所以f(2) + f(4) * + ... + f(2k) = f(1) + f(2) + ... + f(k); * * sum(n) = sum (n / 2) + 1 + 3 + ... + n - 1 = sum (n/2) + n*n/4(n 为偶数) * * = sum (n - 1) + n (n为奇数) * * */ public static long sumOfMaxOdd(long n) { if (n == 1) { return 1; } if (n % 2 == 0) { return sumOfMaxOdd(n / 2) + n * n / 4; } else { return sumOfMaxOdd(n - 1) + n; } } }
#include<iostream> using namespace std; /* 总体思路: 因为奇数的最大奇数约数就是自己啊,对于偶数我们只能一直除2直到得到一个奇数即为最大奇数约数 比如1 2 3 4 5 6 7 8 9 10 即n=10 ,此时奇数有1 3 5 7 9 我们把这几个奇数相加然后n/2 得到第二轮序列序列 1 2 3 4 5 分别对应上次的2 4 6 8 10 五个偶数,这是我们再加1 3 5 依次类推 细节问题: 当n为偶数,就有n/2个奇数,根据等差数列求和公式 即((首项+末项)*项数)/2,我们知道n/2个奇数和为((1+n-1)*n/2)/2, 即为(n/2) * (n/2),此时n为偶数,因此 (n/2) * (n/2) = ((n+1)/2) * ((n+1)/2) 当n为奇数,有(n+1)/2个奇数,此时奇数和为((n+1)/2) * ((n+1)/2) 因此两种情况可以用一个等式来总结 */ //找规律:假设n=16 //while第一次:sum+=1+3+5+7+9+11+13+15(所有奇数的最大奇约数为本身),同时n减半(/2)为8 //while第二次:sum+=1+3+5+7(对应的是16,14,12,10的最大奇公约数),同时n减半为4 //while第三次:sum+=1+3(对应的是8,6的最大奇公约数),同时n减半为2 //while第四次:sum+=1(对应的是4的最大奇公约数),同时n减半为1 //while第五次:sum+=1(对应的是2的最大奇公约数),同时n减半为0 int main() { int n; long long sum = 0; cin >> n; while (n) { for (int i = 1; i <= n; i += 2) sum += i; n /= 2; } cout << sum; system("pause"); return 0; } /*常规方法超时 #include<iostream> using namespace std; int maxOddDivNum(int num) { for (int i = num; i >= 1; i--) { if (num % i == 0 && i % 2 != 0) return i; } return 1; } int main() { int n; while (cin >> n) { int sum = 0; for (int i = 1; i <= n; i++) { sum += maxOddDivNum(i); } cout << sum << endl; } return 0; } */
#include <iostream> using namespace std; int main() { long long n; while(cin>>n){ long long sum = 0; while(n > 0) { if (n%2 == 0) { long long temp = n; while(temp!=1){ temp /= 2; if(temp%2 == 1) break; } sum += temp; n = n-1; } sum += (n+1)*(n+1)/4; n /= 2; } cout<<sum<<endl; } return 0; }