[洛谷P2568]GCD

题目大意:给你$n(1leqslant nleqslant 10^7)$,求$displaystylesumlimits_{x=1}^ndisplaystylesumlimits_{y=1}^n[(x,y)in m 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
", solve(n, n));
	return 0;
}

题解:也可以用也可以用$phi$函数来做。
$$
若(x,y)==p(pin m prime)
Rightarrow ig(dfrac{x}{p},dfrac{y}{p}ig)==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
", ans - ptot);
	return 0;
}
原文地址:https://www.cnblogs.com/Memory-of-winter/p/9517163.html