1. 程式人生 > >A New Function (LightOJ - 1098) || (UVA 10830) (帶一點思維的數論)

A New Function (LightOJ - 1098) || (UVA 10830) (帶一點思維的數論)

We all know that any integer number n is divisible by 1 and n. That is why these two numbers are not the actual divisors of any numbers. The function SOD(n) (sum of divisors) is defined as the summation of all the actual divisors of an integer number n. For example,

SOD(24) = 2+3+4+6+8+12 = 35.

The function CSOD(n) (cumulative SOD) of an integer n, is defined as below:

Given the value of n, your job is to find the value of CSOD(n).

Input

Input starts with an integer T (≤ 1000), denoting the number of test cases.

Each case contains an integer n (0 ≤ n ≤ 2 * 109).

Output

For each case, print the case number and the result. You may assume that each output will fit into a 64 bit signed integer.

Sample Input

3

2

100

200000000

Sample Output

Case 1: 0

Case 2: 3150

Case 3: 12898681201837053

 

Solution:

題目意思我就不說了,很好理解。看到題目的第一眼是不是想把每個數的因子都跑一遍然後求和,很棒的想法,可惜時間複雜的太高了。

然後仔細想想,我們可以從每個數的貢獻度(自己瞎起的名字)的角度考慮,具體思想就是,對於每個1~n的數考慮被加了幾次。例如,我們需要求出n==10的時候2的貢獻度,以2為因子的數有2,4,6,8,10,所以在計算最終的答案的時候2被計算了4次(2不算,因為2本身在此題中不算2的因子),所以對於一個數i,其貢獻度也就是(n / i - 1) * i。ok,這個方法可以說是很不錯了,很遺憾,還是過不了n是1e9的資料。那麼我們可以考慮一下優化,如果時間複雜度可以縮減到sqrt(n)差不多就可以了。

那麼我們就跑到sqrt(n)就可以了,那麼必然會有很多數沒有統計上,怎麼辦呢???彆著急,且聽我慢慢講,對於一個數i,我們考慮到了2 * i, 3 * i, 4 * i, .......等等所以說(n / i - 1) * i 是沒問題的,我們是算了i,不知道你有沒有注意到這些數的因子同時還有2, 3, 4, .......等等,也就是2 * i, 3 * i, ......這些數除了i之外還有2,3,4....這些因子(應該說的很清楚了吧,願你能夠明白。。。)。關鍵就在於這些因子,可能你會想當i == 2,i == 3,i == 4的時候會把這些算上的,沒錯,可是這些說有可能超過sqrt(n)(應該是一定會有超過sqrt(n)的情況,每一個i都會出現這種情況)。

我們再來捋一捋思路,我們從2跑到sqrt(n),算出每個i的貢獻度,即:( n / i - 1) * i, 同時我們考慮到以i為因子的數還有另一個因子k,如果   k <= sqrt(n)   的話在計算後面的數的貢獻度的時候會計算到的,那麼    k  >=  sqrt(n)   的情況就需要我們再計算一下了,我們要計算的k的範圍就是  sqrt(n) + 1 ~ n / i (因為小於n的數中最多有n / i 個 i),而且這些數還是等差數列,公差為1,直接求和公式就好了。

 

之前在做這道題的時候搜的題解感覺寫的並不詳細,看了好久也不明白,所以我儘可能的去詳細寫,希望能對讀者有所幫助。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

typedef long long ll;

int main()
{
    ll n;
    int t, s = 0;
    cin >> t;
    while(t --)
    {
        cin >> n;
        ll m = (ll)sqrt(n);
        ll sum = 0;
        for(int i = 2; i <= m; ++ i)
        {
            ll t1 = n / i;
            sum += (t1 - 1) * i;
            if(t1 > m)        //不加判斷應該也可以,畢竟t1一定是大於等於m的,而且就算與m相等也不影響結果
            {
                sum += (m + 1 + t1) * (t1 - m) / 2;  //求和公式
            }
        }
        printf("Case %d: ", ++s);
        cout << sum << endl;
    }
    return 0;
}