[洛谷P2568]GCD

时间:2024-09-03 17:35:38

题目大意:给你$n(1\leqslant n\leqslant 10^7)$,求$\displaystyle\sum\limits_{x=1}^n\displaystyle\sum\limits_{y=1}^n[(x,y)\in \rm prime]$($(a,b)$为$a,b$的$gcd$)

题解:可以用莫比乌斯反演来做,同这道题,只需要把$m$改成$n$就行了

卡点:

C++ Code:(莫比乌斯反演)

#include <cstdio>
#include <cstring>
#define maxn 10000010
using namespace std;
int n;
int miu[maxn], plist[maxn], ptot;
int g[maxn];
bool isp[maxn];
void sieve(int n) {
memset(isp, true, sizeof isp);
miu[1] = 1;
for (int i = 2; i < n; i++) {
if (isp[i]) plist[ptot++] = i, miu[i] = -1;
for (int j = 0; j < ptot && i * plist[j] < n; j++) {
int tmp = i * plist[j];
isp[tmp] = false;
if (i % plist[j] == 0) {
miu[tmp] = 0;
break;
}
miu[tmp] = -miu[i];
}
}
for (int i = 0; i < ptot; i++) {
for (int j = 1; j * plist[i] < n; j++)
g[j * plist[i]] += miu[j];
}
for (int i = 2; i <= n; i++) g[i] += g[i - 1];
}
inline int min(int a, int b) {return a < b ? a : b;}
long long solve(int n, int m) {
long long ans = 0;
int i, j;
int tmp = min(n, m);
for (i = 1; i <= tmp; i = j + 1) {
j = min(n / (n / i), m / (m / i));
ans += 1ll * (n / i) * (m / i) * (g[j] - g[i - 1]);
}
return ans;
}
int main() {
sieve(maxn);
scanf("%d", &n);
printf("%lld\n", solve(n, n));
return 0;
}

题解:也可以用也可以用$\phi$函数来做。
$$
若(x,y)==p(p\in \rm prime)
\Rightarrow \big(\dfrac{x}{p},\dfrac{y}{p}\big)==1
$$
线性筛出每个数的$\varphi$,再前缀和一下就行了

注意,若$x<y$$(x,y)==p$和$(y,x)==p$是两种不同的方案,但只会在算$y$时被加上,所以答案要乘二,但是当$x==y$时答案会多算一遍,所以要减去质数的个数

卡点:算$\varphi$时没开$long\;long$

C++ Code:(phi函数)

#include <cstdio>
#include <cstring>
#define maxn 10000010
using namespace std;
int n;
bool isp[maxn];
int plist[maxn], ptot;
long long phi[maxn], ans;
void sieve(int n) {
memset(isp, true, sizeof isp);
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (isp[i]) {
plist[ptot++] = i;
phi[i] = i - 1;
}
for (int j = 0; j < ptot && i * plist[j] <= n; j++) {
int tmp = i * plist[j];
isp[tmp] = false;
if (i % plist[j] == 0) {
phi[tmp] = phi[i] * plist[j];
break;
}
phi[tmp] = phi[i] * phi[plist[j]];
}
}
}
int main() {
scanf("%d", &n);
sieve(n);
for (int i = 2; i <= n; i++) phi[i] += phi[i - 1];
for (int i = 0; i < ptot; i++) ans += phi[n / plist[i]] << 1;
printf("%lld\n", ans - ptot);
return 0;
}