1. 程式人生 > >【演算法題】最大的奇約數

【演算法題】最大的奇約數

小易是一個數論愛好者,並且對於一個數的奇數約數十分感興趣。一天小易遇到這樣一個問題: 定義函式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

因為奇數的最大奇數約數就是自己
對於偶數我們只能一直除2直到得到一個奇數即為最大奇數約數

注意:數字較大,使用long long防止溢位

#include <iostream>
#include <vector>
#include <string>
#include <numeric>
#include<algorithm>
using namespace std;
#define debug 1
long long func(int N) { long long result(0); for (auto i = 1; i <= N; ++i) { if ((i & 1) != 0)//i奇數 { result += i; continue; } else//i偶數 { int tmp = i/2; while ((tmp&1)==0) { tmp = tmp / 2
; } result += tmp; } } return result; } int main() { int N; if (debug) { N = 7; } else { cin >> N; } cout << func(N) << endl; return 0; }

但是,上述程式碼會超時,時間複雜度還是過高

需要進一步改進:
例如: 1 2 3 4 5 6 7 8 9 10

即n=10

此時奇數有1 3 5 7 9 我們把這幾個奇數相加
然後: n = n/2
得到第二輪序列序列 1 2 3 4 5 分別對應上次的2 4 6 8 10 五個偶數,這是我們再加1 3 5
依次類推

  • 公式如下:

    • 當n為偶數,就有n/2個奇數,根據等差數列求和公式 即((首項+末項)*項數)/2,我們知道n/2個奇數和為

      ((1+n1)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)
      因此兩種情況可以用一個等式來總結
#include <iostream>
#include <vector>
#include <string>
#include <numeric>
#include<algorithm>
using namespace std;
#define debug 1

long long func(int N)
{
    long long result(0);
    long long tmp;
    for (auto i = N; i > 0; i = i / 2)
    {
        tmp = (i + 1) / 2;
        result += tmp*tmp;
    }
    return result;
}

int main()
{

    int N;
    cin >> N;
    cout << func(N) << endl;

    return 0;
}