[2018蓝桥杯B组决赛] F-矩阵求和

题解

  依据数据范围,暴力枚举自然不可行,则应当转变思维。

  gcd(i, j) = d 表示 (i, j) 的最大公约数为 d,而 count(d) 则表示最大公约数为 d 的数对的个数,count(d) * d * d 则求解的为最大公约数为 d 的所有数的和,此时问题的核心则转化为如何求 count(d)。

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

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

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

  最大公约数的取值范围为 [1, n],d 为 [1, n] 中的任意一个数

       i, j 的取值在 [1, n / d],求解该区间内所有互质对的个数, 而该题的数据范围比较大,故采用筛法求欧拉函数(线性筛法),即 1 - n 中的欧拉函数,时间复杂度能控制在 $O(n)$。

       

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

       上式可进行递推:

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

 

posted @ 2020-11-13 21:33  Fool_one  阅读(258)  评论(0编辑  收藏  举报