部分內容引自清華大學秦嶽《初等數論》,dengyaotriangle的部落格,_WZT_ 的部落格,niiick的部落格
算術基本定理
任何一個大於1的自然數$N$,如果$N$不為質數,那麼$N$可以唯一分解成有限個質數的乘積
$$N=P_1^{a_1}*P_2^{a_2}*P_3^{a_3}......P_n^{a_n}$$
這裡$P1<P2<P3......<Pn$均為質數,其中指數$a_i$是正整數。這樣的分解稱為$N$的標準分解式
素數無限定理
正整數集中包含無限個素數
證明:假設素數有限,設其為$p_1...p_n$,構造$s=1+\prod p_i$,若s是素數,矛盾;若s是合數,則$p_1...p_n$都不是s的約數,與算術基本定理矛盾
Eratosthnes素數篩法
設定一從2開始的數表,若該數沒有被劃掉,則其為一個素數,將其所有倍數劃掉
複雜度$O(nlogn)$
優化版埃氏篩關鍵部分程式碼
p[1]=1; //bool p[i]表示i是否為素數 int sq=sqrt(n); for(int i=2;i<=sq;i++) if(!p[i]) for(int j=i*i;j<=n;j+=i) p[j]=1;
尤拉線性篩法
維護每個數的最小素因子$mindiv$,從2開始,若其$mindiv$未知,則為素數,將其小於本身$mindiv$的素數倍的數設定成素數。每個數只會被其最小素因子篩一次。複雜度$O(n)$
線性篩關鍵部分程式碼
p[1]=1; for(int i=2;i<=n;i++) { if(!p[i]) pr[++cnt]=i; //pr為素數表 for(int j=1;j<=cnt&&pr[j]*i<=n;j++) { p[i*pr[j]]=1; //以pr[j]作為最小質因子更新p[i*pr[j]] if(i%pr[j]==0) break; /*i的mindiv比pr[j]更小,最小質因子不再是pr[j] ,若繼續更新則會出現冗餘操作*/ } }
利用線性篩實現的高效分解質因數
對於數$x$每次取其最小質因子,更新$x$為$x/mindiv$,當$x=1$時分解結束,時間複雜度$O(logn)$
s[1]=1; for(int i=2;i<=n;i++) { if(!s[i]) p[++cnt]=i,g[i]=cnt; for(int j=1;j<=cnt&&i*p[j]<=n;j++) { s[i*p[j]]=1,g[i*p[j]]=j; if(i%p[j]==0) break; } }
樸素分解質因數
$O(\sqrt n)$的分解質因數,對於單個數的分解質因數和積性函式單值求解具有重要作用
for(int i=2;i*i<=n;i++) while(n%i==0) //這裡的i一定是素數,不妨利用反證法證明 //若i為合數,設i有素因子j小於i,j在前面的過程中已被篩去,n%j!=0故n%i!=0,與條件矛盾,故i必為素數 n/=i;
更相減損術
$gcd(a,b)=gcd(a,a-b)$
歐幾里得演算法
$gcd(a,b)=gcd(b,a\%b)$
裴蜀定理(貝祖定理)
若$a,b$是整數,且$(a,b)=d$,那麼對於任意的整數$x,y$,$ax+by$都一定是$d$的倍數,特別地,一定存在整數$x,y$,使$ax+by=d$成立。
推論:$a,b$互質的充要條件是存在整數$x,y$使$ax+by=1$
推論:設$a_1,a_2,a_3......a_n$為$n$個整數,$d$是它們的最大公約數,那麼存在整數$x_1......x_n$使得$x_1*a_1+x_2*a_2+...x_n*a_n=d$
擴充套件歐幾里得演算法(exgcd)
$exgcd$用於解決二元一次不定方程的解的相關問題
對於不定方程$ax+by=c$
由裴蜀定理可知$gcd(a,b)|(ax+by)$,故若$c$不是$gcd(a,b)$的倍數則無整數解
引理:$ax+by=gcd(a,b)$的特解求法
顯然存在$x1,y1$使得$ax+by=bx_1+(a\%b)y_1$成立
由模運算的性質可知$a\%b=a-b*\left\lfloor\dfrac{a}{b}\right\rfloor$
可得$ax+by=bx_1+(a-b*\left\lfloor\dfrac{a}{b}\right\rfloor)y_1$
$=ay_1+b*(x_1-\left\lfloor\dfrac{a}{b}\right\rfloor y_1)$
則有$x=y_1,y=x_1-\left\lfloor\dfrac{a}{b}\right\rfloor y_1$
由歐幾里得演算法得迭代過程的終止狀態為$a=gcd(a,b),b=0$
此時有$gcd(a,b)*x=gcd(a,b)$,取$x=1$即可,此時任取$y$均可保證該式成立,為避免溢位常取$y=0$
該過程的程式碼實現
void exgcd(int a,int b,int &x,int &y) { if(!b) { x=1,y=0; return; } exgcd(b,a%b,y,x); y-=x*(a/b); }
設$x_0,y_0$為$ax+by=gcd(a,b)$的一組特解,即有$ax_0+by_0=gcd(a,b)$
則有原方程的一組特解
$$a\frac{x_0c}{gcd(a,b)}+b\frac{y_0c}{gcd(a,b)}=c$$
$$x_1=\frac{x_0c}{gcd(a,b)},y_1=\frac{y_0c}{gcd(a,b)}$$
構造原方程的通解形式
易得對於任意常數$d$,有$a(x_1+db)+b(y_1-da)=c$成立。為保證通解均為整數解,只需使$db,da$均為整數即可;而為保證得到通解,$d$須取滿足上述條件的最小值,而易得該最小值為$\frac{1}{gcd(a,b)}$
可得通解:$x=x_1+\frac{kb}{gcd(a,b)},y=y_1+\frac{ka}{gcd(a,b)}$其中$k$為任意整數
並有性質:隨$k$增大,$x$增大,$y$減小
則有最小正整數解$(x\%d_x+d_x)\%d_x$($d_x$為上文所提$db$)(若上式結果為0則最小正整數解為$d_x$),此時$y$取到兩數均為正整數時的最大值
乘法逆元
$ab≡ba≡1(mod\ p)$
則稱$b$是$mod\ p$意義下$a$的乘法逆元(定義了剩餘系中的除法),記為$a^{-1}$,$a$的乘法逆元的意義是$a$在$mod\ p$剩餘系下的倒數
乘法逆元的擴充套件歐幾里得求法
給定$a,p$計算$b$使得$a*b≡1(mod\ p)$,等價於解方程$ax+py=1$($x,y$為整數變數,擴充套件歐幾里得即可)
條件:$a,p$互質,即二元一次不定方程有解的條件,也即乘法逆元存在的充要條件
if(exgcd(a,mod,x,y)==1) //判斷逆元是否存在 printf("%d\n",(x%mod+mod)%mod); //xj=即為a的逆元
時間複雜度$O(logn)$
費馬小定理
假如$p$是質數,且$gcd(a,p)=1$,那麼$a^{p-1}≡1(mod\ p)$
證明:
引理:集合$S1=\{1,2,3…p-1\},S2=\{1a,2a,3a…(p-1)a\}$,$S1$在模$p$意義下等於$S2$
故$(p-1)!≡a^{p-1}*(p-1)! (mod\ p)$,$(p-1)!$與$p$互素存在乘法逆元,故$a^{p-1}≡1(mod\ p)$。
乘法逆元的快速冪求法
若$p$是素數,由於$a^{p-1}≡1(mod\ p)$,故$a$的乘法逆元為$a^{p-2}$(就一個快速冪,所以這裡就不再提供程式碼了)
條件:$a,p$互質且$p$為質數;時間複雜度:$O(logn)$
線性預處理乘法逆元
在$O(n)$的時間內求出$1..n$的逆元,條件:$1...n$均與$p$互素(為簡化表達常表達為$p$為質數)
顯然$1^{-1}≡1(mod\ p)$
設$p=\left\lfloor\dfrac{p}{k}\right\rfloor*k+r(0<r<k<p)$
可得$\left\lfloor\dfrac{p}{k}\right\rfloor*k+r≡0(mod\ p)$
兩邊同乘$k^{-1},r^{-1}$可得
$\left\lfloor\dfrac{p}{k}\right\rfloor*r^{-1}+k^{-1}≡0(mod\ p)$
可得$k^{-1}≡-\left\lfloor\dfrac{p}{k}\right\rfloor*(p\ mod\ k)^{-1}(mod\ p)$
由此可以實現遞推求解逆元
a[1]=1; for(int i=2;i<=n;i++) { a[i]=-(p/i)*a[p%i]; a[i]=(a[i]%p+p)%p; }
積性函式
在數論中的積性函式:對於正整數$n$的一個算術函式$f(n)$,若$f(1)=1$,且當$a,b$互質時$f(ab)=f(a)f(b)$,在數論上就稱它為積性函式。若對於某積性函式$f(n)$,就算$a,b$不互質,也有$f(ab)=f(a)f(b)$,則稱它為完全積性的。
尤拉函式
對正整數$n$,尤拉函式$\varphi(n)$是小於等於$n$的正整數中與$n$互質的數的數目,例如$\varphi(8)=4(1,3,5,7)$
由尤拉函式的積性可得$φ(n)=\prodφ(p_i^{q_i})$
$φ(x=p^q)=p^{q-1}*(p-1)=x*(1-\frac{1}{p})$($p$為質數)
這個結論是顯然的。
故$φ(x)=x*\prod\limits_{i=1}^n(1-\frac{1}{p_i})$
由$φ$的積性性質可由線性篩法$O(n)$計算$φ(1)...φ(n)$
p[1]=1,phi[1]=1; for(int i=2;i<=maxn-10;i++) { if(!p[i]) pr[++cnt]=i,phi[i]=i-1; for(int j=1;j<=cnt&&pr[j]*i<=maxn-10;j++) { p[i*pr[j]]=1; if(i%pr[j]==0) { phi[i*pr[j]]=phi[i]*pr[j]; break; } phi[i*pr[j]]=phi[i]*phi[pr[j]]; } }
尤拉定理
若$p,a$為正整數,且$p,a$互質,則:$a^{φ(p)}≡1(mod\ p)$
推論:$(a^{φ(p)})^{-1}=a^{φ(p)}$
推論:$a^b≡a^{b\%φ(p)}≡a^{b-k*φ(p)}(gcd(a,p)=1)$(等式兩邊同乘$(a^{φ(p)})^{-1}$)
擴充套件尤拉定理
$b>=φ(p)$時,有$a^b≡a^{b\%p+p}(mod\ p)$
($b<φ(p)$時往往直接快速冪求解即可)
中國剩餘定理(CRT)
中國剩餘定理可用於求解同餘方程組
$\begin{cases}x≡a_1(mod\ m_1)\\x≡a_2(mod\ m_2)\\.....\\x≡a_n(mod\ m_n)\end{cases}$
其中$m_1,m_2...m_n$為兩兩互質的整數
求解過程:
我們設$M=\prod\limits_{i=1}^nm_i\ ,\ M_i=\frac{M}{m_i}\ ,\ M_it_i≡1(mod\ m_i)\ (1<=i<=n)$
則該方程組必有一解$x_0=\sum\limits_{i=1}^na_iM_it_i$
可得通解$x=x_0+k*M$($k$為任意整數)
則有最小正整數解$x_{min}=(x\%M+M)\%M$,上式結果為0時$x_{min}=M$
證明:
$\begin{cases}a_jM_jt_j≡0(mod\ m_i)(i\ne j)\\a_jM_jt_j≡a_j(mod\ m_i)(i=j)\end{cases}$
故$\sum\limits_{i=1}^na_iM_it_i≡a_i(mod\ m_i)$
擴充套件中國剩餘定理(EXCRT)
擴充套件中國剩餘定理可用於求解同餘方程組
$\begin{cases}x≡a_1(mod\ m_1)\\x≡a_2(mod\ m_2)\\.....\\x≡a_n(mod\ m_n)\end{cases}$
和中國剩餘定理的區別在於,擴充套件中國剩餘定理不要求$m_i$兩兩互質(很強的性質)
考慮遞推求解,假設已經求出前$k-1$個方程組成的同餘方程組的一個解$x$,設$M=lcm(m_1,m_2...m_{k-1})$
則前$k-1$的方程的通解為$x+i*M$($i$為任意整數)
那麼對於加入第$k$個方程後的方程組
我們就是要求一個正整數$t$,使得$x+t*M≡a_k(mod\ m_k)$
即$t*M≡a_k-x(mod\ m_k)$
對於這個式子我們已經可以通過擴充套件歐幾里得求解$t$
若該同餘式無解,則整個方程組無解,若有,則前$k$個同餘式組成的方程組的一個解為$x_k=x+t*M$
核心程式碼
scanf("%lld%lld%lld",&n,&a,&b); m=a,ans=b; for(int t,tmp,i=2;i<=n;ans%=m,i++) { scanf("%lld%lld",&a,&b); int g=exgcd(m,a,t,tmp),c=((b-ans)%a+a)%a; if(c%g) { printf("-1\n"); return 0; } t=mul(t,c/g,a/g),ans+=t*m,m=lcm(m,a); } ans=(ans%m+m)%m; printf("%lld\n",ans?ans:m);
大步小步演算法(BSGS)
$BSGS$主要用於求解形如$a^x≡b(mod\ p)$的方程,其中$gcd(a,p)=1$
考慮最小解$x_0$,由尤拉定理的推論可得$a^x≡a^{x-k*φ(p)}$,則$x_0\in[0,φ(p)),k\in Z$
設$a^{x=ik-t}≡b(mod\ p)$,則必有$a^{ik}≡b*a^t(mod\ p)(0<t<=k,0<i<=\left\lceil\dfrac{φ(p)}{k}\right\rceil)$
列舉$t$,將$b*a^t\%p$存入雜湊表(這裡也可以使用$map$,不過會多帶一個$log$),列舉$i$,在雜湊表中查詢即可得到第一個迴圈節中的所有解。第一個查詢到的解即為最小解,對於第一個迴圈節中的任一解$x$,顯然$x+k*φ(p),k\in Z^*$為該方程的解
時間複雜度$O(k+\dfrac{φ(p)}{k})$,由均值不等式可得$k=\sqrt{φ(p)}$時有最低時間複雜度$O(\sqrt{φ(p)})$
最小解求解程式碼
k=ceil(sqrt(phi)); int tp=1; for(int tmp=n*b%p,i=1;i<=k;i++,(tp*=b)%=p,(tmp*=b)%=p) mp[tmp]=i; for(int tmp=tp,i=1;i<=(phi+k-1)/k;i++,(tmp*=tp)%=p) if(mp[tmp]) { printf("%lld\n",i*k-mp[tmp]); return 0; } printf("no solution\n");
擴充套件BSGS
擴充套件$BSGS$主要用於求解形如$a^x≡b(mod\ p)$的方程
對於同餘方程$a^x≡b(mod\ p)$,我們可以將其展開為$a*a^{x-1}+pk=b$的形式,可得$gcd(a,p)|b,x>0$為原方程有整數解的一個充分條件,這時討論$x=0$等式是否成立,若成立則求得答案,否則若方程無解則同餘方程無解
對於有解的方程,兩邊同除$gcd(a,p)$可得$\frac{a}{gcd(a,p)}*a^{x-1}+\frac{p}{gcd(a,p)}*k=\frac{b}{gcd(a,p)}$
轉化為同餘式可得$\frac{a}{gcd(a,p)}a^{x-1}≡\frac{b}{gcd(a,p)}(mod\ \frac{p}{gcd(a,p)})$
設$p'=\frac{p}{gcd(a,p)}$,若$gcd(a,p')=1$,按$BSGS$處理即可,否則重複執行上述過程
顯然遞迴深度不超過$logp$,故可近似認為擴充套件$BSGS$與$BSGS$時間複雜度相同
完整程式碼
#include<iostream> #include<cstdio> #include<cmath> #include<map> #define int long long using namespace std; int c,a,p,b,k,ans,phi; map<int,int>mp; int gcd(int x,int y) { return y?gcd(y,x%y):x; } signed main() { while(1) { scanf("%lld%lld%lld",&a,&p,&b); if(!a) return 0; a%=p,mp.clear(),ans=0,c=1; int ps=p,bs=b; bool f=0; if(b>=p) { printf("No Solution\n"); continue; } if(!a) { printf("0\n"); continue; } for(int g=gcd(a,p);g!=1;g=gcd(a,p)) { if(c==b) { printf("%lld\n",ans),f=1; break; } if(b%g) { printf("No Solution\n"),f=1; break; } p/=g,(c*=a/g)%=p,b/=g,ans++; } if(f) continue; phi=p; for(int tmp=p,i=2;i*i<=tmp;i++) if(tmp%i==0) { while(tmp%i==0) tmp/=i; phi=phi*(i-1)/i; } k=ceil(sqrt(phi)); int tp=1; for(int tmp=b*a%p,i=1;i<=k;i++,(tp*=a)%=p,(tmp*=a)%=p) mp[tmp]=i; for(int tmp=tp,i=1;i<=(phi+k-1)/k;i++,(tmp*=tp)%=p) if(mp[tmp*c%p]) { printf("%lld\n",i*k-mp[tmp*c%p]+ans),f=1; break; } if(!f) printf("No Solution\n"); } return 0; }