阶
假设 \(\gcd(a,p)=1\),如果 \(a^x\equiv 1\pmod p\),那么最小的 \(x\) 称之为 \(a\) 在模 \(p\) 意义下的阶,记作 \(\delta_p(a)\)。
在抽象代数中,这里的“阶”就是模 \(p\) 缩剩余系关于乘法形成的群中,元素 \(a\) 的阶。记号 \(\delta\) 表示阶也只用于这个特殊的群。
性质
- \(a^1,a^2,\dots,a^{\delta_p(a)}\) 模 \(p\) 两两不同余。
- 如果 \(a^n\equiv 1\pmod p\),那么 \(\delta_p(a) \mid n\)。同时有 \(a^n \equiv a^m \pmod p\) 那么 \(n\equiv m \pmod {\delta_p(a)}\)。
- \(\delta_p(ab)=delta_p(a) delta_p(b)\) 的充要条件是 \(\gcd(delta_p(a),delta_p(b))=1\)
- \(\delta_p(a^k)=\dfrac{\delta_p(a)}{\gcd(\delta_p(a),k)}\)(*)
以上均可以使用定义或者反证法证明。
原根
假设 \(\gcd(a,p)=1\),如果 \(\delta_p(a)=\varphi(p)\),那么 \(a\) 是模 \(p\) 意义下的原根。
也就是说,如果 \(a\) 是模 \(p\) 的原根,那么 \(a^1,a^2,\dots,a^{\varphi(p)}\) 模 \(p\) 两两不同。
判定定理
对于 \(\gcd(a,p)=1\),如果 \(\varphi(p)\) 的所有质因子 \(q\) 都有 \(a^{\varphi(p)/q}\not\equiv 1\pmod p\),那么 \(a\) 是模 \(p\) 意义下的原根。
必要性显然,充分性可以反证。
数量
如果 \(p\) 存在一个原根 \(g\),那么
\[\delta_p(g^k)=\dfrac{\delta_p(g)}{\gcd(\delta_p(g),k)}=\dfrac{\varphi(p)}{\gcd(varphi(g),k)} \]如果 \(\gcd(\varphi(p),k)=1\),那么 \(g^k\bmod p\) 就是 \(p\) 的原根。
所以 \(p\) 如果有原根,那么就有 \(\varphi(\varphi(p))\) 个。
原根存在定理
\(p\) 存在原根当且仅当 \(p=2,4,q^\alpha,2q^{\alpha}\),其中 \(q\) 为质数,\(\alpha \in \N\)
证明
最小原根
可以证明 \(p\) 如果有原根,那么最小原根是小于 \(p^{0.25}\) 级别的。不过其实当 \(p\) 增大,最小原根不是多项式级别,所以我们可以直接暴力找最小原根。
模板题
Link
找一个数的所有原根。
其实只需要找到最小原根 \(p\),然后只要满足 \(\gcd(\varphi(p),k)=1\),那么 \(g^k\bmod p\) 就是原根。
#include<cstdio>
#include<algorithm>
#define db double
#define gc getchar
#define pc putchar
#define U unsigned
#define ll long long
#define ld long double
#define ull unsigned long long
#define Tp template<typename _T>
#define Me(a,b) memset(a,b,sizeof(a))
Tp _T mabs(_T a){ return a>0?a:-a; }
Tp _T mmax(_T a,_T b){ return a>b?a:b; }
Tp _T mmin(_T a,_T b){ return a<b?a:b; }
Tp void mswap(_T &a,_T &b){ _T tmp=a; a=b; b=tmp; return; }
Tp void print(_T x){ if(x<0) pc('-'),x=-x; if(x>9) print(x/10); pc((x%10)+48); return; }
#define EPS (1e-7)
#define INF (0x7fffffff)
#define LL_INF (0x7fffffffffffffff)
#define N 1000000
#define maxn 1000039
#define maxm
#define MOD
#define Type int
#ifndef ONLINE_JUDGE
//#define debug
#endif
using namespace std;
Type read(){
char c=gc(); Type s=0; int flag=0;
while((c<'0'||c>'9')&&c!='-') c=gc(); if(c=='-') c=gc(),flag=1;
while('0'<=c&&c<='9'){ s=(s<<1)+(s<<3)+(c^48); c=gc(); }
if(flag) return -s; return s;
}
int isp[maxn],pr[maxn],phi[maxn],minx[maxn],cnt;
void init(){
int i,j; for(i=2;i<=N;i++) minx[i]=INF;
for(i=2;i<=N;i++){
if(!isp[i]) pr[++cnt]=i,phi[i]=i-1,minx[i]=i;
for(j=1;j<=cnt&&i*pr[j]<=N;j++){
isp[i*pr[j]]=1; minx[i*pr[j]]=mmin(minx[i*pr[j]],mmin(pr[j],minx[i]));
if(i%pr[j]==0){ phi[i*pr[j]]=phi[i]*pr[j]; break; }
phi[i*pr[j]]=phi[i]*(pr[j]-1);
}
} phi[1]=1,isp[1]=1; return;
}
int n,d,ans[maxn],num;
int check(int x){
if(x==2||x==4) return 0;
if(x%2==0) x/=2; if(x%2==0) return 1;
int i=minx[x]; while(x%i==0) x/=i;
return x!=1;
}
int gcd(int x,int y){ if(!x||!y) return x|y; return gcd(y,x%y); }
int fastpow(int x,int y){
int res=1,tmp=x;
while(y){
if(y&1) res=1ll*res*tmp%n;
y>>=1,tmp=1ll*tmp*tmp%n;
} return res;
}
int js(int x){
if(gcd(x,n)!=1) return 0;
int cur,tmp,las; cur=tmp=phi[n],las=-1;
while(cur!=1){
while(cur!=1&&minx[cur]==las) cur/=minx[cur];
if(cur==1) return 1;
if(fastpow(x,tmp/minx[cur])==1) return 0;
las=minx[cur]; cur/=minx[cur];
} return 1;
}
void work(){
n=read(); d=read(); if(check(n)){ pc('0'),pc('\n'),pc('\n'); return; }
int i,g; num=0; for(i=1;i<n;i++) if(js(i)){ g=i; break; }
for(i=1;i<=phi[n];i++) if(gcd(i,phi[n])==1) ans[++num]=fastpow(g,i);
sort(ans+1,ans+num+1); print(num),pc('\n');
for(i=d;i<=num;i+=d) print(ans[i]),pc(' '); pc('\n'); return;
}
int main(){
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
init(); int T=read(); while(T--) work(); return 0;
}
标签:return,cur,原根,笔记,学习,varphi,delta,define
From: https://www.cnblogs.com/jiangtaizhe001/p/17165884.html