Longge's problem POJ - 2480
原创
©著作权归作者所有:来自51CTO博客作者tizzi的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、内容
二、思路
- 通过打表我们可以发现,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;
}