题意:给出一个奇素数,求出他的原根的个数,多组数据。
首先何为原根:设 m m 是正整数,是整数,若 m m 模的阶等于 ϕ(m) ϕ ( m ) ,则称 a a 为模的一个原根(其中 ϕ(m) ϕ ( m ) 表示 m m 的欧拉函数)。
假设一个数对于 p p 来说是原根,那么的结果两两不同,且有 1<g<p,0<i<p 1 < g < p , 0 < i < p ,那么 g g 可以称为是的一个原根。
可以表示为 gi mod p≠gj mod p g i m o d p ≠ g j m o d p ( p p 为素数),并且且 0<i,j<p 0 < i , j < p 之间,则 g g 为的原根。
解决这个问题我们要使用以下定理:
如果
p
p
有原根,则它恰有个不同的原根(无论
p
p
是否为素数都适用)
因为题目给出的是一个奇素数,又因为对于一个素数, ϕ(p)=p−1 ϕ ( p ) = p − 1 ,所以我们最终要求的就是 ϕ(p−1) ϕ ( p − 1 ) 。
我们可以线性的筛出欧拉函数,具体操作与线性筛素数类似。
以下的三个性质是线性筛欧拉的关键,以下所有的p均为素数。
1.
ϕ(p)=p−1
ϕ
(
p
)
=
p
−
1
2.若
i mod p=0,则ϕ(i∗p)=ϕ(i)∗p
i
m
o
d
p
=
0
,
则
ϕ
(
i
∗
p
)
=
ϕ
(
i
)
∗
p
3.若
i mod p≠0,那么ϕ(i∗p)=ϕ(i)∗(p−1)
i
m
o
d
p
≠
0
,
那
么
ϕ
(
i
∗
p
)
=
ϕ
(
i
)
∗
(
p
−
1
)
最后粘上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
long long phi[1000000],num,prime[1000000],n;
bool pri[1000000];
void get_phi(int n)
{
memset(pri,true,sizeof(pri));
memset(phi,0,sizeof(phi));
phi[1]=1;
pri[1]=false;
for(int i=2;i<=n;++i)
{
if(pri[i])//找到一个质数
{
prime[++num]=i;
phi[i]=i-1;//性质1
}
for(int j=1;j<=num,prime[j]*i<=n;++j)
{
pri[i*prime[j]]=false;
if(i%prime[j]==0)//性质2
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else//性质3
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int main()
{
get_phi(100005);
while(cin>>n)
cout<<phi[n-1]<<endl;
return 0;
}