结论:我数论太渣了……
言归正传……先列出几个常用的性质/结论
同余式:
1. da≡db (mod m) 则a≡b (mod m/(m,d) ) (这在取遍剩余系会用到)
2. a≡b (mod m) m'|m , a≡b (mod m')
3. a≡b (mod mi) i=1..k 等价于 a≡b (mod M) M=[m1,m2,..mk]
一般剩余系:
剩余系就是可能余数组成的集合,简化剩余系也称既约剩余系,是模n的完全剩余系的一个子集,其中每个元素与n互素。
1. 模p的剩余系{rk},若(p,d)=1那么{d*rk}也是模p的剩余系
这个很重要,如果求ax mod p的既约剩余系的大小,我们只要利用同余式的性质1
转化为x*a/gcd(a,p) mod (p/gcd(a,p)),根据这个理论 x取值个数就是p/gcd(a,p)
2. 设模m1的既约剩余系为R1,m2的既约剩余系为R2,m1m2互质,则模m1m2的既约剩余系为R={x=m2x1+m1x2 (mod m1m2) | x1∈R1,x2∈R2}
这个结论证明可以考虑任意两个x互不同余即可(作差判断)
这就告诉我们一个很厉害的东西 S(M)表示M既约剩余系的大小的话,S(M)=S(m1)*S(m2)
3. 这个结论推广到k维就是,设模mk的既约剩余系为Rk, mk两两互质,M=∏mi 则模M的既约剩余系为R={ ∑M*mi-1*ai (mod M) | xi∈Ri }
因此以后我们求模M的剩余系大小,可以对M质因数分解分别求出后再相乘
阶、原根、指标:
阶(又叫指数):a,m为互素整数且m>=2 ,则使 ar≡1(mod m) 成立的最小正整数 r 记为 a 模 m 的指数,记作ord_m(a)
根据欧拉定理,显然ord_m(a)一定是φ(m)的约数
阶有几个性质: 1. ord_m(a)=xy 则 ord_m(ax)=y 2.ord_m(a)=x, ord_m(b)=y (x,y)=1 则ord_m(ab)=xy;
3. ord_m(a)=t ord_m(ax)=t/gcd(t,x) (其实就是性质1的变形)
原根:阶中要求(a,m)互素,所以a^x与m互素,所以ax模m的余数与m互素,这样这个余数只可能有 φ(m) 种不同的取值。
(这里我想到了了一个很有意思的结论:1~n内与n互质的数的和为{n*φ(n)+[n==1]} /2
这里考虑gcd(i,n)=1,则gcd(n,n-i)=1,证明用反证法即可。)
当满足ord_m(a)=φ(m)的a称作模m的原根,记作g。
这有一个显然的结论若 x=0,1,2,3,...,φ(m)-1,则 gx 模 m 的余数都和 m 互素,并且模 m 两两不同余。
指标:简单来说,就是一切小于m且与m互质的数r,都存在唯一个数k(k<φ(m)),使得gk≡r (mod m)
我们就把k叫作r的指标,记作I(r)
这是一个非常有用的性质,之后的题目我们会经常看到他。
指标有这么几个性质:1. I(ab)≡I(a)+I(b) (mod m) 2. I(ak)≡kI(a) (mod m)
简而言之,指标就像是数论里的取对数。
原根和求指标将在后面的同余方程中起到巨大作用,但是首先我们要知道什么数有原根
直接上结论: 当且仅当m=2,4,pk,2pk时才有原根,p是某一奇素数
证明可以看《初等数论及其应用》
还有一个非常有用的定理是:若g是某一奇素数p的原根,那么g也是pk的原根,证明依然在《初等数论及其应用》
事实上,一个数p的原根的数目为φ(φ(p)) (考虑若g是p的原根,gu是p的原根当且仅当gcd(u,φ(p))=1)
另外我们注意到2的原根是1,4的原根是3,但其他2次幂是没有原根的(在高次剩余里,模2次幂会带来不小的麻烦)
事实上有一个恒等式:aφ(xy)/2≡1 (mod xy) (x,y互素且a与xy互素)
在2次幂的时候就有a^(2k)≡1 (mod 2k+2) 恒成立,所以2的3次及以上次幂没有原根
(补充一个很有意思的结论:5模2k+2的指数正好是2k )
一个数的原根不止一个,一般就找最小的那个就可以了
其他的原根可用最小原根的gu表示(gu是p的原根当且仅当gcd(u,φ(p))=1)
找m的最小原根一般就是暴力枚举g,然后穷举m的每个质因数p,判断gφ(m)/p≡1 (mod m)是否成立(成立的话就不是原根)
一道找所有原根的题目
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 typedef long long ll; 5 int phi[1000010],p[1000010],a[100010],ans[1000010],n,r,t; 6 bool v[1000010]; 7 8 int gcd(int a,int b) 9 { 10 return (b==0)?a:gcd(b,a%b); 11 } 12 13 ll quick(ll x,int y,int mo) 14 { 15 ll s=1; 16 while (y) 17 { 18 if (y&1) s=s*x%mo; 19 x=x*x%mo; 20 y>>=1; 21 } 22 return s; 23 } 24 25 int check(int n) 26 { 27 if (n%2==0) return -1; 28 if (!v[n]) return n; 29 for (int i=2; p[i]*p[i]<=n; i++) 30 if (n%p[i]==0) 31 { 32 while (n%p[i]==0) n/=p[i]; 33 if (n>1) return -1; 34 else return p[i]; 35 } 36 return -1; 37 } 38 39 bool work(int g,int mo) 40 { 41 if (gcd(g,mo)!=1) return 0; 42 for (int i=1; i<=r; i++) 43 if (quick(g,phi[mo]/a[i],mo)==1) return 0; 44 return 1; 45 } 46 47 int getg(int mo) 48 { 49 int n=phi[mo]; 50 r=0; 51 for (int i=1; p[i]*p[i]<=n; i++) 52 if (n%p[i]==0) 53 { 54 while (n%p[i]==0) n/=p[i]; 55 a[++r]=p[i]; 56 } 57 if (n>1) a[++r]=n; 58 for (int i=2; i<mo; i++) 59 if (work(i,mo)) return i; 60 } 61 62 int main() 63 { 64 phi[1]=1; 65 for (int i=2; i<=1000000; i++) 66 { 67 if (!v[i]) 68 { 69 phi[i]=i-1; 70 p[++t]=i; 71 } 72 for (int j=1; j<=t; j++) 73 { 74 if (i*p[j]>1000000) break; 75 v[i*p[j]]=1; 76 if (i%p[j]==0) 77 { 78 phi[i*p[j]]=phi[i]*p[j]; 79 break; 80 } 81 else phi[i*p[j]]=phi[i]*(p[j]-1); 82 } 83 } 84 while (scanf("%d",&n)!=EOF) 85 { 86 if (n==2) 87 { 88 puts("1"); 89 continue; 90 } 91 if (n==4) 92 { 93 puts("3"); 94 continue; 95 } 96 int ch=(n%2==1)?check(n):check(n/2); 97 if (ch==-1) 98 { 99 puts("-1"); 100 continue; 101 } 102 int g=getg(n); 103 int l=0; 104 for (int i=1; i<=phi[n]; i++) 105 if (gcd(i,phi[n])==1) ans[++l]=quick(g,i,n); 106 sort(ans+1,ans+1+l); 107 for (int i=1; i<=l; i++) 108 { 109 printf("%d",ans[i]); 110 if (i==l) puts(""); else printf(" "); 111 } 112 } 113 }