算法1
筛法求欧拉函数
筛法求欧拉函数(适用于1~n求欧拉的情况)
① 先写出线性筛算法的模板。
② 考虑特殊情况:若该数是质数pp的话,那么该数的欧拉函数就是p−1p−1。
③ 每个数的欧拉函数与质因子的次数无关。例如:N=2100×3100N=2100×3100,但是N的欧拉函数还是N×(1−12)×(1−13)N×(1−12)×(1−13)
④ 若imodprimes[j]==0imodprimes[j]==0,由于primes[j]primes[j]是ii的一个质因子,并且在计算ii的欧拉函数的时候已经计算了primes[j]primes[j]出现的情况(1−1primes[j])(1−1primes[j]),所以φ(primes[j]×i)=primes[j]×φ(i)φ(primes[j]×i)=primes[j]×φ(i)。
⑤ 若imodprimes[j]!=0imodprimes[j]!=0,primes[j]primes[j]就一定是primes[j]×iprimes[j]×i的最小质因子,而且primes[j]primes[j]不包含在ii的质因子当中。由于φ(i)=i×(1−1p1)×(1−1p2)×…×(1−1pk)φ(i)=i×(1−1p1)×(1−1p2)×…×(1−1pk),所以φ(primes[j]×i)=primes[j]×i×(1−1p1)×(1−1p2)×…×(1−1pk)×(1−1primes[j])φ(primes[j]×i)=primes[j]×i×(1−1p1)×(1−1p2)×…×(1−1pk)×(1−1primes[j])。最终就可以得到φ(primes[j]×i)=primes[j]×φ(i)×(1−1primes[j])=φ(i)×(primes[j]−1)φ(primes[j]×i)=primes[j]×φ(i)×(1−1primes[j])=φ(i)×(primes[j]−1)。
算法核心代码
int primes[N], cnt; // primes[]存储所有素数
int euler[N]; // 存储每个数的欧拉函数
bool st[N]; // st[x]存储x是否被筛掉
void get_eulers(int n)
{
euler[1] = 1;
for (int i = 2; i <= n; i ++ )
{
if (!st[i])
{
primes[cnt ++ ] = i;
euler[i] = i - 1;
}
for (int j = 0; primes[j] <= n / i; j ++ )
{
int t = primes[j] * i;
st[t] = true;
if (i % primes[j] == 0)
{
euler[t] = euler[i] * primes[j];
break;
}
euler[t] = euler[i] * (primes[j] - 1);
}
}
}
C++ 代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1000010;
int primes[N], cnt; //primes数组存所有的质因子,cnt是下标
int phi[N]; //欧拉函数
bool st[N]; //表示每个数是否被筛掉了
LL get_eulers(int n)
{
phi[1] = 1;
for (int i = 2; i <= n; i ++) //线性筛
{
if (!st[i]) //当前没有被筛过就是质数
{
primes[cnt ++] = i;
phi[i] = i - 1;
}
for (int j = 0; primes[j] <= n / i; j ++)
{
st[primes[j] * i] = true;
if (i % primes[j] == 0)
{
phi[primes[j] * i] = phi[i] * primes[j];
break;
}
phi[primes[j] * i] = phi[i] * (primes[j] - 1);
}
}
LL res = 0;
for (int i = 1; i <= n; i ++)
res += phi[i];
return res;
}
int main()
{
int n;
cin >> n;
cout << get_eulers(n) << endl;
return 0;
}