阶 δm(a)
定义: gcd(a,m)=1∧aδm(a)≡1(modm)
性质:
- ai≡aj(modm)(1≤i,j≤δm(a))
- an≡1(modm)→δm(a)∣nandax≡ay(modm)→x≡y(modδm(a))
- gcd(a,m)=gcd(b,m)=1→(δm(ab)=δm(a)δm(b)⟺gcd(δm(a),δm(b))=1)
- gcd(a,m)=1→δm(ak)=gcd(δm(a),k)δm(a)
原根a
定义: gcd(a,m)=1∧δm(a)=ϕ(m)∧aϕ(m)≡1(modm)
判定定理: ∀p∣ϕ(m)apϕ(m)≡1(modm)
原根个数: ϕ(ϕ(m))
所有原根: {minrt1,minrt2,...minrtj,...,minrtϕ(m)}∧gcd(j,ϕ(m))=1
引理1: ∃g 是模 m 的原根, gp−1≡1(modp2)
引理2: ∀β∈N∗∧p∣kβ,gϕ(pβ)=1+pβ×kβ
原根存在定理: m=2,4,pa,2pa∀p∈{oddprime}
证明
阶性质一
ai≡aj(modm)(1≤i,j≤δm(a))
反证法
若 ai≡aj(modm)(1≤i,j≤δm(a))
则 ai−j≡aδm(a)≡1(modm)
∵{i,j}∈δm(a)
∴i−j<δm(a) ,与定义冲突不成立,所以性质成立
阶性质二
an≡1(modm)→δm(a)∣nandax≡ay(modm)→x≡y(modδm(a))
∵aδm(a)≡1(modm)∧ax≡y≡1(modm)(x<δm(a))
∴∏aδm(a)≡a∑δm(a)≡∏1≡an≡1(modm)
∴δm(a)∣n
且 ax∗∏aδm(a)≡ax+∑δm(a)≡y×1≡1(modm) ,性质二(1)得证
与上面最后一行类似
∵ay−x≡1≡aδm(a)(modm)
∴δm(a)∣(y−x)
∴x≡y(modδm(a)) ,性质二(2)得证
阶性质三
gcd(a,m)=gcd(b,m)=1→(δm(ab)=δm(a)δm(b)⟺gcd(δm(a),δm(b))=1)
必要性
∵aδm(a)≡1(modm)andbδm(b)≡1(modm)
∴(ab)lcm(δm(a),δm(b))≡1(modm)
∴gcd(a,m)=gcd(b,m)=1→gcd(ab,m)=1
∴(ab)δm(ab)≡1(modm)
由于阶性质二得: δm(ab)∣lcm(δm(a),δm(b))
∵δm(ab)=δm(a)δm(b)
∴δm(a)δm(b)∣lcm(δm(a),δm(b))
∴δm(a)δm(b)∣lcm(δm(a),δm(b))→δm(a)δm(b)=lcm(δm(a),δm(b))→gcd(δm(a),δm(b))=1
充分性
上同,略证 δm(ab)∣lcm(δm(a),δm(b))
gcd(δm(a),δm(b))→lcm(δm(a),δm(b))=δm(a)×δm(b)
∴δm(ab)∣δm(a)δm(b)
又 ∵(ab)δm(ab)≡(ab)δm(a)δm(b)≡(aδm(a))δm(b)(bδm(b))δm(a)≡1(modm)
∴δm(a)∣δm(ab)andδm(b)∣δm(ab)→δm(a)δm(b)∣δm(ab)
综上 ∴δm(ab)∣δm(a)δm(b)andδm(a)δm(b)∣δm(ab)→δm(ab)=δm(a)δm(b)
阶性质四
gcd(a,m)=1→δm(ak)=gcd(δm(a),k)δm(a)
(ak)δm(ak)≡akδm(ak)≡1(modm)→δm(a)∣kδm(ak)→gcd(δm(a),k)δm(a)∣δm(ak)
(ak)gcd(δm(a),k)δm(a)≡(aδm(a))gcd(δm(a),k)k≡aδm(a)≡1(modm)→δm(ak)∣gcd(δm(a),k)δm(a)
∴δm(ak)=gcd(δm(a),k)δm(a)
原根判定定理
∀p∣ϕ(m)apϕ(m)≡1(modm)
设对于这种条件下, ∃a 不是模 m 的原根
则 ∃t<ϕ(m)at≡1(modm)
∵Beˊzout′sidentity ∴∃x,yxt−yϕ(m)=gcd(t,ϕ(m))→xt=gcd(t,ϕ(m))+yϕ(m)
at≡akt≡agcd(t,ϕ(m))+yϕ(m)≡agcd(t,ϕ(m))×ayϕ(m)≡1(modm)
∵EulerTheorem , ∴aϕ(m)≡1(modm)→agcd(t,ϕ(m))≡1(modm)
∵gcd(t,ϕ(m))∣ϕ(m)∧gcd(t,ϕ(m))≤t<ϕ(m)
∴∃p∣ϕ(m)∧p∈{oddprime},gcd(t,ϕ(m))=pϕ(m)
∴agcd(t,ϕ(m))≡apϕ(m)≡1(modm) 矛盾
∴ 假设不成立
原根个数
ϕ(ϕ(m))
所有原根
{minrt1,minrt2,...minrtj,...,minrtϕ(m)}∧gcd(j,ϕ(m))=1
若 ∃m 的原根a,阶性质四 →δm(ak)=gcd(δm(a),k)δm(a)=gcd(ϕ(m),k)ϕ(m)
∴gcd(ϕ(m),k)=1→δm(ak)=ϕ(m)→δm(ak) 也是模 m 的原根
∑[gcd(k,ϕ(m))=1]=ϕ(ϕ(m))
引理1
∃g 是模 m 的原根, gp−1≡1(modp2)
设 g 是模 m 的原根,且 (g+p)p−1 满足条件
(g+p)p−1≡Cp−10gp−1+Cp−21gp−1p≡gp−1+p(p−1)gp−2≡p2gp−2+gp−1−pgp−2≡0+1−pgp−2≡1(modp2)
引理2
∀β∈N∗∧p∣kβ,gϕ(pβ)=1+pβ×kβ
β=1 显然成立
设对 β 成立,需证对 β+1 也成立
gϕ(pβ+1)=gϕ(pβ×p)=gϕ(pβ)p=(1+pβ×kβ)p≡1+pβ+1×kβ(modpβ+2)
结合 p∣kβ 成立
原根存在定理
m=2,4,pa,2pa∀p∈{oddprime}
m=2,4
显然,证略
m=pa
对于 p
由阶性质三的必要性证明 gcd(a,m)=gcd(b,m)=1→δm(ab)∣lcm(δm(a),δm(b))
即 ∃c,δm(c)=lcm(δm(a),δm(b))
对 1∼p−1 两两进行 lcm 并做入上转化得: ∃c,δp(c)=lcm(δp(1),δp(2),...,δp(p−1))
∴∀j∈{1,2,...,p−1},δp(j)∣δp(c)
是 xδp(c)≡1(modp) 的根
∵Lagrangetheorem→δp(c)≤ϕ(p)
∵Fermat′slittletheorem→δp(c)≥ϕ(p)
∴δp(c)=ϕ(p)
∴c 是模 p 的原根
对于 pa
∵EulerTheorem,∴δpa(g)∣pα−1(p−1)
设 δpα(g)=pα−1(p−1),1≤β≤α
由引理一二 ⟶gϕ(pβ)≡1+pβ×kβ≡1(modpβ+1)⟶gδpα(g)≡1(modpβ+1)
gδpα(g)≡1(modpα)→β≥α
∴β=α
∴δpα(g)=pα−1(p−1)=ϕ(pα)
∴g 是模 pα 的原根
m=2pa
∵ϕ(pa)=ϕ(2pa)
∴ 得证
原根求法
首先要判断一个数是否有原根
这个可以通过预处理得到(在欧拉筛的时候顺带最后根据 prime 数组推一遍就行
has_rt[2] = has_rt[4] = 1;
for ( ll i = 1; i < prime.size(); i ++ ) {
for ( ll j = 1; j * prime[i] < N; j *= prime[i] ) has_rt[j * prime[i]] = 1;
for ( ll j = 2; j * prime[i] < N; j *= prime[i] ) has_rt[j * prime[i]] = 1;
}
1
2
3
4
5
既然所有的原根都是最小原根 minrt 的整数幂且一共就 ϕ(ϕ(m)) 个
那么我们可以先求出来 minrt 然后通过遍历 1→ϕ(m) 获取所有的原根
对于最小的原根我们可以从 1 往后枚举,根据原根判定定理去查询(对于 ϕ(m) 的质因数我们也可以通过预处理实现
vector<ll> sep;
inline void Seperator ( ll x ) {
x = phi[x];
for ( ll i = 2; i * i <= x; i ++ ) {
if ( x % i == 0 ) sep.push_back(i);
while ( x % i == 0 ) x /= i;
}
if ( x > 1 ) sep.push_back(x);
}
inline bool Check ( ll x, ll n ) {
if ( ksm ( x, phi[n], n ) != 1 ) return 0;
for ( ll i = 0; i < sep.size(); i ++ ) if ( ksm ( x, phi[n] / sep[i], n ) == 1 ) return 0;
return 1;
}
inline ll get_Min_Rt ( ll x ) {
for ( ll i = 1; i <= x; i ++ ) if ( Check ( i, x ) ) return i;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
然后根据这个 minrt 求得所有的原根
vector<int> rt;
inline void get_All_Rt ( ll minrt, ll n ) {
ll cur = 1;
for ( ll i = 1; i <= phi[n]; i ++ ) {
cur = cur * minrt % n;
if ( gcd(i, phi[n]) == 1 ) rt.push_back(cur);
}
}
1
2
3
4
5
6
7
8
这样我们就成功获取啦
总程序
const ll N = 1e6 + 10;
namespace Number {
bool has_rt[N], not_prime[N];
vector<ll>prime;
ll phi[N];
inline void Sieve () {
not_prime[0] = not_prime[1] = phi[1] = 1;
for ( ll i = 2; i < N; i ++ ) {
if ( !not_prime[i] )
phi[i] = i - 1,
prime.push_back(i);
for ( ll j = 0; j < prime.size() && i * prime[j] < N; j ++ ) {
not_prime[i * prime[j]] = 1;
if ( i % prime[j] == 0 ) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
} else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
has_rt[2] = has_rt[4] = 1;
for ( ll i = 1; i < prime.size(); i ++ ) {
for ( ll j = 1; j * prime[i] < N; j *= prime[i] ) has_rt[j * prime[i]] = 1;
for ( ll j = 2; j * prime[i] < N; j *= prime[i] ) has_rt[j * prime[i]] = 1;
}
}
inline ll ksm ( ll a, ll b, ll mod ) { ll res = 1; while ( b ) { if ( b & 1 ) res = res * a % mod; a = a * a % mod; b >>= 1;} return res; }
inline ll gcd ( ll a, ll b ) { return b ? gcd(b, a % b) : a; }
vector<ll> sep, rt;
inline void Seperator ( ll x ) {
x = phi[x];
for ( ll i = 2; i * i <= x; i ++ ) {
if ( x % i == 0 ) sep.push_back(i);
while ( x % i == 0 ) x /= i;
}
if ( x > 1 ) sep.push_back(x);
}
inline bool Check ( ll x, ll n ) {
if ( ksm ( x, phi[n], n ) != 1 ) return 0;
for ( ll i = 0; i < sep.size(); i ++ ) if ( ksm ( x, phi[n] / sep[i], n ) == 1 ) return 0;
return 1;
}
inline ll get_Min_Rt ( ll x ) {
for ( ll i = 1; i <= x; i ++ ) if ( Check ( i, x ) ) return i;
return 0;
}
inline void get_All_Rt ( ll minrt, ll n ) {
ll cur = 1;
for ( ll i = 1; i <= phi[n]; i ++ ) {
cur = cur * minrt % n;
if ( gcd(i, phi[n]) == 1 ) rt.push_back(cur);
}
}
}using namespace Number;
int main () {
ios::sync_with_stdio(false); Sieve ();
ll cass; cin >> cass; while ( cass -- ) {
ll n; cin >> n;
if ( has_rt[n] ) {
rt.clear(); sep.clear();
Seperator ( n );
get_All_Rt ( get_Min_Rt(n), n );
sort ( rt.begin(), rt.end() );
cout << rt.size() << endl;
for ( auto i : rt ) cout << i << " ";
cout << endl;
} else {
cout << 0 << endl << endl;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73