原根 学习笔记
阶
假设 ,如果 ,那么最小的 称之为 在模 意义下的阶,记作 。
在抽象代数中,这里的“阶”就是模 缩剩余系关于乘法形成的群中,元素 的阶。记号 表示阶也只用于这个特殊的群。
性质
- 模 两两不同余。
- 如果 ,那么 。同时有 那么 。
- 的充要条件是
- (*)
以上均可以使用定义或者反证法证明。
原根
假设 ,如果 ,那么 是模 意义下的原根。
也就是说,如果 是模 的原根,那么 模 两两不同。
判定定理
对于 ,如果 的所有质因子 都有 ,那么 是模 意义下的原根。
必要性显然,充分性可以反证。
数量
如果 存在一个原根 ,那么
如果 ,那么 就是 的原根。
所以 如果有原根,那么就有 个。
原根存在定理
存在原根当且仅当 ,其中 为质数,
证明
最小原根
可以证明 如果有原根,那么最小原根是小于 级别的。不过其实当 增大,最小原根不是多项式级别,所以我们可以直接暴力找最小原根。
模板题
Link
找一个数的所有原根。
其实只需要找到最小原根 ,然后只要满足 ,那么 就是原根。
#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; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析