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