**~~
**~~
数学彩笔继续自救之路
本文不讨论任何证明,对证明有兴趣移步这里
原根的性质:
1.不是所有的整数都有原根。但所有的素数都有原根。
2.如果 a 与 m 是互质的整数且n>0,则如果a是模m的一个原根,那么整数a, a^2, … , a^φ(m)构成模m的既约剩余系。(既约剩余类,即简化剩余类,是指在每个模m的值与m互质的剩余类中,各取一数组成的集合)
3.当正整数m有原根时,有φ(φ(m))个原根。
4.对所有φ(m)的素因子i,若a^(φ(m)/i) mod m 均不为1 则a为模m的一个原根(用来找模m的原根)
5.模m有原根的充要条件是m= 1,2,4,p^n, 2*p^n。其中p是奇质数,n是任意正整数。(用来判断一个数有没有原根)
6.模m的最小原根小于等于m^0.25。
7.模m的所有原根之积mod m值为1。
8.模m的全体原根之和mod m = 莫比乌斯函数μ(n-1)(我还不会这个我记他干嘛)
9.找到最小原根,对所有小于φ(m)互素的k,a^k mod m也为模m的原根(用来得到最小原根后求一个数的所有原根)
模板:
检查一个数有无原根
bool check_proot(int p)//先打素数表
{
if(n==2||n==4) return true;
if(p%2==0) p/=2;
if(is_prime[p]) return true;
if(p%2==0) return false;
repi(i,2,cntp)if(p%prime[i]==0){
while(p%prime[i]==0) p/=prime[i];
return p==1;
}
return false;
}
找最小原根
int find_proot_min(int n)//模n的最小原根 最大的话从n-1倒序即可 保证有原根再调用
{
repi(i,2,n-1)if(qpow(i,phi[n],n)==1){//首先首先i^phi[n]==1(mod n)
bool flag=true;
repi(j,1,pos)if(qpow(i,phi[n]/divi[j],n)==1){//判是不是原根
flag=false; break;
}
if(flag) return i;
}
}
找所有原根
int now=1,proot= //proot为该数最小原根
ans.clear();
repi(i,1,phi[n]-1){
now=1ll*now*root%n;
if(__gcd(i,phi[n])==1) ans.pb(now);
}
sort(all(ans)); ans.erase(unique(all(ans)),ans.end());//去重
例题:
1.poj1284 性质3
const int MAX_N=1e5+5;
int phi[MAX_N];
void phi_table(int n)
{
repi(i,2,n) phi[i]=0;
phi[1]=1;
repi(i,2,n)if(!phi[i]){
for(int j=i;j<=n;j+=i){
if(!phi[j]) phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
}
int main()
{
phi_table(65536);
int n;
while(~si(n)) printf("%d\n",phi[phi[n]]);
return 0;
}
/*
素数一定有原根
模m有原根时有phi[phi[m]]个原根
*/
2.hdu4992 判一个数有无原根,有的话找出其所有原根
性质5判有无 性质4找最小 性质9找所有
const int MAX_N=1e6+5;
ll qpow(ll x,ll n,ll mod){ ll res=1;while(n){ if(n&1) res=(res*x)%mod; x=x*x%mod,n>>=1; } return res; }
int phi[MAX_N];
void phi_table(int n)
{
repi(i,2,n) phi[i]=0;
phi[1]=1;
repi(i,2,n)if(!phi[i]){
for(int j=i;j<=n;j+=i){
if(!phi[j]) phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
}
bool is_prime[MAX_N];
int prime[MAX_N/10],cntp=0;
void init()
{
for(ll i=2;i<MAX_N;++i) is_prime[i]=1;
for(ll i=2;i<MAX_N;++i){
if(is_prime[i]){
prime[++cntp]=i;
for(ll j=i*i;j<MAX_N;j+=i)
is_prime[j]=0;
}
}
}
bool check(int p)
{
if(p%2==0) p/=2;
if(is_prime[p]) return true;
if(p%2==0) return false;
repi(i,2,cntp)if(p%prime[i]==0){
while(p%prime[i]==0) p/=prime[i];
return p==1;
}
return false;
}
int divi[MAX_N],pos;
vector<int> ans;
int main()
{
phi_table(MAX_N-1),init();
int n;
while(~si(n))
{
if(n==2) puts("1");
else if(n==4) puts("3");
else if(!check(n)) puts("-1");//判是否存在原根(2,4, p^n,2*p^n p为奇素数)
else{
pos=0;
int tmp=phi[n];
repi(i,1,cntp){
if(prime[i]*prime[i]>tmp) break;
if(tmp%prime[i]==0) divi[++pos]=prime[i];
while(tmp%prime[i]==0) tmp/=prime[i];
}//处理出素因子,之后找原根用
if(tmp!=1) divi[++pos]=tmp;
int now=1,root;
repi(i,2,n-1)if(qpow(i,phi[n],n)==1){//首先i^phi[n]==1(mod n)
bool flag=true;
repi(j,1,pos)if(qpow(i,phi[n]/divi[j],n)==1){//再根据性质找最小满足的原根
flag=false; break;
}
if(flag){
root=i;break;
}
}
ans.clear();
repi(i,1,phi[n]-1){
now=1ll*now*root%n;
if(__gcd(i,phi[n])==1) ans.pb(now);//根据性质处理出其它的
}
sort(all(ans)); ans.erase(unique(all(ans)),ans.end());//去重
int len=ans.size();
repi(i,0,len-1) printf("%d%c",ans[i],ce(i,len-1));
}
}
return 0;
}