神秘数学题

题目:给定 \(n\),求 \(\displaystyle \sum_{i=1}^{n} \sum_{i=1}^{n} \gcd(i,j)\)

原题地址:

参考洛谷 P2303 [SDOI2012] Longge 的问题。这题我们可以直接枚举对答案的贡献 \(\gcd\)

具体来说,就是当两数 \(i,j\) 的最大公约数为 \(x\) 的时候,\(\frac{i}{x}\)\(\frac{j}{x}\) 互质。因此,我们就可以枚举这个 \(x\),统计它对答案贡献了多少次,就可以把原式变成下面这样。

\[\begin{aligned} \large \sum_{k=1}^{n} k\sum_{p=1}^{\lfloor \frac{n}{k} \rfloor} \sum_{q=1}^{\lfloor \frac{n}{k} \rfloor} [\gcd(p,q)=1] \end{aligned} \]

因为 \(i,j\) 必须为 \(k\) 的倍数,所以我们直接枚举是 \(k\) 的几倍,也就是 \(p,q\),这时候 \(\frac{i}{k}=p,\frac{j}{k}=q\)

为了把式子转化成欧拉函数,我们做这样的变换。

\[\begin{aligned} \large \sum_{k=1}^{n} 2k\sum_{p=1}^{\lfloor \frac{n}{k} \rfloor} \sum_{q=1}^{p-1} [\gcd(p,q)=1]+k \end{aligned} \]

这是因为对于对于 \(p,q\) 都从 \(1\) 取到 \(n\) 的时候,一个 \((x,y)(x<y)\) 会被取两次,即 \((p=x,q=y),(p=y,q=x)\)。而 \((x,x)\) 只会被取一次,当 \(x=1\) 时,贡献为 \(1\),否则贡献为 \(0\)

现在变成这样。

\[\begin{aligned} \large \sum_{k=1}^{n} 2k\sum_{p=1}^{\lfloor \frac{n}{k} \rfloor} \varphi(p)-k \end{aligned} \]

很明显,时间复杂度为调和级数,所以这个时间复杂度是 \(O(n\log n)\) 的。

发现我们要查询的 \(\varphi\) 和都是一段,于是我们直接做前缀和,令 \(\displaystyle g(x)=\sum_{i=1}^{x} \varphi(i)\)

答案表示为:

\[\begin{aligned} \large \sum_{k=1}^{n} 2kg(\lfloor \frac{n}{k} \rfloor)-k \end{aligned} \]

直接预处理 \(O(n)\) 即可计算。

#include <bits/stdc++.h>
#define upp(a, x, y) for (int a = x; a <= y; a++)
#define dww(a, x, y) for (int a = x; a >= y; a--)
#define pb(x) push_back(x)
#define endl '\n'
#define x first
#define y second
#define PII pair<int, int>
using namespace std;
const int EU = 5e7 + 10, X = 998244353;
int st[EU], phi[EU], sumphi[EU];
int prs[EU],top;
void init(int n) {
    phi[1] = 1;
    upp(i, 2, n) {
        if (!st[i]) {
            prs[top++]=i;
            phi[i] = i - 1;
        }
        for (int j = 0; prs[j] <= n / i; j++) {
            st[i * prs[j]] = 1;
            if (i % prs[j] == 0) {
                phi[i * prs[j]] = prs[j] * phi[i];
                break;
            } else
                phi[i * prs[j]] = (prs[j] - 1) * phi[i];
        }
    }
    upp(i, 1, n) sumphi[i] = (sumphi[i - 1] + phi[i]) % X;
    return;
}
int n, ans;
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n;
    init(n);
    upp(k, 1, n) {
        ans = ((ans + (2 * (long long)k * sumphi[n / k]) % X - k) % X + X) % X;
    }
    cout << ans << endl;
    return 0;
}
posted @ 2025-05-10 18:19  PM_pro  阅读(24)  评论(0)    收藏  举报