阶与原根学习笔记

V.阶与原根

实际上这部分内容在OI中应用很少,但它是一些重要思想以及算法的基础。

阶是在互质数 \((a,m)\) 间的定义:满足 \(a^n\equiv1\pmod m\) 的最小 \(n\) 被称作 \(a\)\(m\) 的阶,记作 \(\delta_m(a)\)

明显,在 \(a,m\) 不互质时,\(a\) 的无论多少次幂全都是 \(\gcd(a,m)\) 的倍数,故不可能取到 \(1\);而当互质时,依据扩展欧拉定理,必有 \(a^{\varphi(m)}\equiv1\pmod m\),但也不排除存在更小的 \(n\)

阶有什么性质呢?

性质1.\(a^1,a^2,\dots,a^{\delta_a(m)}\)\(m\) 两两不同余。

如果两个数 \(a^i,a^j\) 同余的话(不妨设 \(i<j\)),则 \(a^i\times(a^{j-i}-1)\equiv0\pmod m\),而 \(a^i\not\equiv0\pmod m\),故只有可能 \(a^{j-i}-1\equiv0\pmod m\),即 \(a^{j-i}\equiv0\pmod m\),而 \(j-i\) 一定是 \(<\delta_a(m)\) 的,故此时 \(\delta_a(m)\) 就不是最小的满足 \(a^n\equiv1\pmod m\)\(n\) 了,与前提矛盾。

性质2.若 \(a^n\equiv1\pmod m\),则 \(\delta_m(a)|n\)

\(f(n)=a^n\bmod m\) 这个函数,依照性质1,其最短循环节是 \(\delta_m(a)\)。故所有模 \(m\)\(1\) 的数一定只存在于 \(\delta_m(a)\) 倍数的位置。


一个数 \(g\) 被称作 \(m\) 的原根,当且仅当 \(\delta_m(g)=\varphi(m)\)

显然,其前提条件是 \(\gcd(g,m)=1\),不然 \(\delta_m(g)\) 不存在。

原根判定定理:\(g\)\(m\) 的原根,当且仅当对于一切 \(\varphi(m)\) 的质因数 \(p\),都有 \(g^{\varphi(m)/p}\not\equiv1\pmod m\)

原根个数定理:若 \(m\) 有原根,则原根个数是 \(\varphi\Big(\varphi(m)\Big)\)

原根存在定理:\(m\) 有原根,当且仅当 \(m\in\{2,4,p^\alpha,2p^\alpha\}\),其中 \(p\) 是奇质数,\(\alpha\in\N^+\)

最小原根级别定理:若 \(m\) 有原根,则最小原根是 \(\sqrt[4]{m}\) 级别的。

什么?你问证明?但是考试也不考证明啊(摊手)

原根寻找方法:暴力找到最小原根 \(g\),则对于所有与 \(\varphi(m)\) 互质的 \(x\)\(g^x\) 都是 \(m\) 的原根。

时间复杂度 \(O\Bigg(\log m\Big(\sqrt[4]{m}+\varphi\big(\varphi(m)\big)\Big)+\varphi(m)\Bigg)\)

总结步骤:

  1. 判断 \(m\) 是否 \(\in\{2,4,p^\alpha,2p^\alpha\}\),不是则直接返回。
  2. 预处理 \(\varphi(m)\) 所有质因数。
  3. 从小到大枚举与 \(m\) 互质的 \(i\),并依次用快速幂检验 \(\varphi(m)\) 所有质因数。
  4. 找到最小原根,枚举所有与 \(\varphi(m)\) 互质的数,求快速幂即可。

V.I.【模板】原根

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1000000;
int T,n,d,pri[1001000],phi[1001000],mnp[1001000];
int ksm(int x,int y){
	int z=1;
	for(;y;y>>=1,x=1ll*x*x%n)if(y&1)z=1ll*z*x%n;
	return z;
}
void sieve(){
	for(int i=2;i<=N;i++){
		if(!pri[i])pri[++pri[0]]=i,phi[i]=i-1,mnp[i]=i;
		for(int j=1;j<=pri[0]&&i*pri[j]<=N;j++){
			pri[i*pri[j]]=true,mnp[i*pri[j]]=pri[j];
			if(i%pri[j])phi[i*pri[j]]=phi[i]*phi[pri[j]];
			else{phi[i*pri[j]]=phi[i]*pri[j];break;}
		}
	}
}
vector<int>factorize(int ip){
	vector<int>ret;
	while(ip!=1)ret.push_back(mnp[ip]),ip/=mnp[ip];
	sort(ret.begin(),ret.end()),ret.resize(unique(ret.begin(),ret.end())-ret.begin());
	return ret;
}
bool checkrootexist(int ip){
	if(n==2||n==4)return true;
	vector<int>v=factorize(ip);
	if(v.size()==1&&v[0]!=2)return true;
	if(ip&1)return false;
	v=factorize(ip/2);
	return v.size()==1&&v[0]!=2;
}
vector<int>fp;
bool checkisroot(int ip){
	for(auto i:fp)if(ksm(ip,phi[n]/i)==1)return false;
	return true;
}
vector<int>res;
void findallroots(int ip){
	for(int i=1;i<=phi[n];i++)if(__gcd(i,phi[n])==1)res.push_back(ksm(ip,i));
	sort(res.begin(),res.end());
} 
int main(){
	sieve();
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&d),res.clear();
		if(!checkrootexist(n)){puts("0\n");continue;}
		fp=factorize(phi[n]);
		for(int i=1;;i++)if(__gcd(i,n)==1&&checkisroot(i)){findallroots(i);break;}
		printf("%d\n",res.size());
		for(int i=d-1;i<res.size();i+=d)printf("%d ",res[i]);puts("");
	}
	return 0;
}

原文地址:https://www.cnblogs.com/Troverld/p/14620919.html