[2018藍橋杯B組決賽] F-矩陣求和

題解ios

  依據數據範圍,暴力枚舉天然不可行,則應當轉變思惟。函數

  gcd(i, j) = d 表示 (i, j) 的最大公約數爲 d,而 count(d) 則表示最大公約數爲 d 的數對的個數,count(d) * d * d 則求解的爲最大公約數爲 d 的全部數的和,此時問題的核心則轉化爲如何求 count(d)。spa

       $gcd(i, j) = d, gcd(\frac{i}{d}, \frac{j}{d}) = 1$。 code

       令 $i = \frac{i}{d}, j = \frac{j}{d}$。blog

       代入則 $gcd(i, j) = 1$。ci

  最大公約數的取值範圍爲 [1, n],d 爲 [1, n] 中的任意一個數get

       i, j 的取值在 [1, n / d],求解該區間內全部互質對的個數, 而該題的數據範圍比較大,故採用篩法求歐拉函數(線性篩法),即 1 - n 中的歐拉函數,時間複雜度能控制在 $O(n)$。io

       

  $s[n] = \sum_{i = 2}^n Euler(i) \times 2 + 1$ class

       上式可進行遞推:stream

       $s[i] = s[i - 1] + 2 \times Euler[i]$

#include <iostream>
using namespace std;

typedef long long LL;
const int N = 1e7 + 10, mod = 1e9 + 7;
int n, cnt;
LL primes[N], eulers[N], s[N];
bool st[N];

// 線性篩法 
void get_eulers(int n)
{ 
    eulers[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!st[i]) {
            primes[cnt++] = i;
            // i 爲質數 
            eulers[i] = i - 1;
        }
        // 篩非質數 
        for (int j = 0; primes[j] <= n / i; ++j) {
            int t = primes[j] * i;
            st[t] = true;
            if (i % primes[j] == 0) {
                eulers[t] = eulers[i] * primes[j];
                break;
            }
            eulers[t] = eulers[i] * (primes[j] - 1);
        }
    }
    // 開 LL,避免溢出  
    s[1] = 1; 
    // 遞推求解 s[i]
    for (int i = 2; i <= n; ++i) {
        s[i] = s[i - 1] + 2 * eulers[i];
    } 
}

int main()
{
    cin >> n;
    get_eulers(n);
    int res = 0;
    for (int d = 1; d <= n; ++d) {
        res = (res + (LL) s[n / d] * d % mod * d) % mod;
    }
    cout << res << endl;
    return 0;
}
相關文章
相關標籤/搜索