Luogu P1390 公约数的和

[ exttt{Description} ]

给出 (n),求 (sumlimits_{i = 1}^nsumlimits_{j=i+1}^n gcd(i,j))

[ exttt{Solution} ]

注意到:

[sumlimits_{i = 1}^nsumlimits_{j = i + 1}^n gcd(i, j) = frac{sumlimits_{i = 1}^nsumlimits_{j = 1}^n gcd(i, j) - sumlimits_{i = 1}^n gcd(i,i)}{2} = frac{sumlimits_{i = 1}^nsumlimits_{j = 1}^n gcd(i, j) - frac{n cdot (n + 1)}{2}}{2} ]

于是问题转化为如何求出 (sumlimits_{i = 1}^nsumlimits_{j = 1}^n gcd(i, j))

考虑枚举 (d = gcd(i, j)),则式子化为:

[sumlimits_{d = 1}^n d cdot sumlimits_{i = 1}^nsumlimits_{j = 1}^n [gcd(i,j) = d] ]

[sumlimits_{d = 1}^n d cdot sumlimits_{i = 1}^{frac{n}{d}}sumlimits_{j = 1}^{frac{n}{d}} [gcd(i,j) = 1] ]

(g(n) = sumlimits_{i = 1}^nsumlimits_{j = 1}^n [gcd(i,j) = 1]),则式子化为:

[sumlimits_{d = 1} ^ n d cdot g(frac{n}{d}) ]

考虑化简 (g(n)),注意到:

[varphi(n) = sumlimits_{i = 1}^n [gcd(n, i) = 1] ]

则有:

[g(n) = 2 cdot sumlimits_{i = 1}^n varphi(i) - 1 ]

线性筛 (mathcal{O(n)}) 预处理出欧拉函数,再求一遍欧拉函数前缀和,即可 (mathcal{O(1)}) 计算 (g(n))

至此 (sumlimits_{d = 1} ^ n d cdot g(frac{n}{d})) 已经可以 (mathcal{O(n)}) 计算,可以进一步用 " 数论分块 " 优化到 (O(sqrt n))

[ exttt{Code} ]

#include <cstdio>

using namespace std;

const int N = 2000100;

int n;

int m, prime[N], fac[N];
int phi[N];
long long S[N];

void GetPrimes(int N) {
	phi[1] = 1;
	for (int i = 2; i <= N; i ++) {
		if (!fac[i]) fac[i] = i, prime[++ m] = i, phi[i] = i - 1;
		for (int j = 1; j <= m; j ++) {
			if (prime[j] > fac[i] || prime[j] > N / i) break;
			fac[i * prime[j]] = prime[j];
			phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
		}
	}

	for (int i = 1; i <= N; i ++)
		S[i] = S[i - 1] + phi[i];
}

long long g(int n) {
	return 2 * S[n] - 1;
}

int main() {
	GetPrimes(2000000);

	scanf("%d", &n);

	long long ans = 0;
	for (int x = 1, Nx; x <= n; x = Nx + 1) {
		Nx = n / (n / x);
		ans += 1ll * (x + Nx) * (Nx - x + 1) / 2 * g(n / x);
	}

	ans -= 1ll * n * (n + 1) / 2;
	ans /= 2;

	printf("%lld
", ans);

	return 0;
}

[ exttt{Thanks} exttt{for} exttt{reading} ]

原文地址:https://www.cnblogs.com/cjtcalc/p/13124835.html