神秘数学题
题目:给定 \(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;
}

浙公网安备 33010602011771号