一、内容

Longge

二、思路

  • 通过打表我们可以发现,gcd(i, N)的结果都N的约数,那么我们便可以从此出发。
  • 我们枚举 N的所有约数,设d为N的某一个约数,那么1~N中有gcd(i,N) == d,我们将数都除以一个d,那么就等于gcd(i/d, N/d) = 1, 故我们只需要求解出【1,N/d】 范围内和N/d互质的数(欧拉函数)。
  • 那么题目就转化为: 枚举所有约数d, 求 phi(N/ d)的值,最后结果乘上一个d就可以了。
  • 我们可以预先处理出5万以内的质数和欧拉函数的值,而要求大于5万的数的欧拉值的话,可以通过已经求解出的质数来求,这样会快很多。

三、代码

#include <cstdio>
#include <algorithm>
#include <iostream>
typedef long long ll;
using namespace std;
const int N = 5e4; //提前筛出sqrt(2^31 - 1)的质数
int p[N + 5], phi[N + 5], n, cnt;//p是记录质数, phi记录欧拉函数 
bool v[N + 5]; //false为质数 
void primes() {
	v[1] = true;
	phi[1] = 1;
	for (int i = 2; i <= N; i++) {
		if (!v[i]) p[cnt++] = i, phi[i] = i - 1;
		for (int j = 0; p[j] <= N / i; j++) {
			v[i * p[j]] = true;
			if (i % p[j] == 0) {
				phi[i * p[j]] = p[j] * phi[i];
				break;
			}  
			phi[i * p[j]] = (p[j] - 1) * phi[i];
		} 
	} 
} 
int euler(int n) {
	if (n < N) {
		return phi[n];
	}
	int ans = n; 
	//通过质数筛出欧拉值
	for (int j = 0; p[j] <= n / p[j]; j++) {
		if (n % p[j] == 0) {
			ans = ans / p[j] * (p[j] - 1);
			while (n % p[j] == 0) n /= p[j];
		}
	} 
	if (n > 1) {
		ans = ans / n * (n - 1);
	}
	return ans;
}
int main() {
	primes();
	while (scanf("%d", &n) != EOF) {
		ll ans = 0; 
		//通过试除法求出所有的约数
		for (int i = 1; i <= n / i; i++) {
			if (n % i == 0) {
				//值为d * phi(n / d)
				ans += i * euler(n / i);
				if (i != n / i) {
					ans += n / i * euler(n / (n / i));
				}
			}
		} 
		printf("%lld\n", ans);
	}
	return 0;
}