算法:线性筛求欧拉函数,本来还想杜教筛
本文主要给刚学线性筛欧拉函数的萌新看。神仙勿喷QwQ
(large{ exttt{Meaning}})
给定一个 (n) ,求 (F(n)) :有多少组数对 ([a,b](0<a<ble n)) 其中 (a,b) 互质。
(large{ exttt{Solution}})
将 (F(n)) 中的数对归类,设 (f(n)) 为 (b) 为 (n) 的数对 ([a,b]) 的个数,发现 (f) 和 (varphi) (欧拉函数,即 (varphi(n)=sum_{i=1}^{n} [ gcd (n,i) ==1]) )意义是相同的,那么即 (F(n)=sum^{n}_{i=1} varphi(n)) 。
然后可以用线性筛求出 (varphi) ,再作一次前缀和,得到 (F) 数组,结束了。
还是说说怎么线性筛求欧拉函数吧。
首先,我们有这么一个性质:
(varphi(n)=n*prod_{p|n} (p-1)/p ~~~(pin exttt{prime}))
先设 (f(n)=prod_{p|n} (p-1)/p ~~~(pin exttt{prime}))
若一个质数 (p) 整除 (n) ,则 (f(pn)=f(n)) ,因为 (pn) 和 (n) 用质因数的种类相同( (p) 不是一个新质因数)。
则有
[varphi(pn)=varphi(n)*p
]
若一个质数 (p) 不整除 (n) ,则 (f(pn)=f(n)*(p-1)/p) ,因为p是一个新的质因数。
则有
[varphi(pn)=varphi(n)*p*(p-1)/p= varphi(n)*(p-1)
]
发现这些式子在线性筛里面可以使用(每个数只会被筛到一次),只需要添上几行代码,且不会增加复杂度。(具体操作参考代码)
(large{ exttt{Code}})
#include <iostream>
#include <cstdio>
using namespace std;
// #define PB push_back
// #define MP make_pair
// const int M=200010;
#define int long long
// #define us unsigned
// #define LL long long
const int N = 1000000;
// const int mod = 998244353;
int a, ans, prime[N + 10], phi[N + 10], top;
inline void init()//线性筛欧拉函数
{
phi[1] = 1;//phi[1]较特殊
for (int i = 2; i <= N; i++)
{
if (!phi[i])
{
prime[++top] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= top && i * prime[j] <= N; j++)
{
if (i % prime[j] == 0)
{
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
for (int i = 1; i <= N; i++)//前缀和
phi[i] += phi[i - 1];
}
signed main()
{
init();
// freopen("in","r",stdin);
while (~scanf("%lld", &a))
{
if (!a)
break;
printf("%lld
", phi[a] - phi[1]);//注意phi[1]取不了。
}
return 0;
}