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;
}

 

 

猜你喜欢

转载自blog.csdn.net/aqa2037299560/article/details/84885836