突然發現之前也寫過一篇文章叫“簡單數學”,緣,妙不可言
博主整理了一下\(OI\)中簡單的數論知識,故取名簡單數學,以后會隨着學習的深入慢慢擴展開來。
以下的代碼部分沒經過驗證,可能一定會出鍋
質數
質數的判定
顧名思義就是判定某一個數是不是質數
試除法
一個結論:
若一個正整數\(N\)為合數,那么一定存在一個正整數\(T\)能夠整除\(N\),且\(2\le N \le \sqrt N\)
該結論可以通過反證法證明,這里不再贅述(直接背過他不香嗎?)
bool judge_prime(int N){
for(int i=2;i*i<=N;++i)
if(N%i==0) return 0;
return 1;
}
容易看出復雜度為\(O( \sqrt N)\)
質數的篩選
顧名思義就是曬出出\(1\backsim N\)中的質數
Eratosthenes篩法
核心思想就是質數的倍數是合數,具體不再贅述
void primes(int n){
memset(v,0,sizeof(v));
for(int i=2;i<=n;++i)
{
if(v[i]) continue;
prime[++cnt]=i;//i是質數
for(int j=i*2;j<=n;j+=i) v[j]=1;
}
}
復雜度為\(O(\sum_{質數p\le \frac{N}{p}})=O(N\ \log \log N)\)
線性篩法
核心思想就是通過“從小到大積累質因子”的方式來標記合數,以此來避免\(Eratosthenes\)篩法重復標記合數的問題
具體實現原理簡單來說就是:每個合數\(i*p\)只會被它的最小質因子篩一次
void primes(int n){
memset(v,0,sizeof(v));//最小質因子
cnt=0;//質數的數量
for(int i=2;i<=n;++i)
{
if(v[i]==0) {prime[++cnt]=i;v[i]=i;}
for(int j=1;j<=cnt;++j)
{
if(prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
}
}
}
//於2020.7.6更正代碼
復雜度為\(O(N)\)我一般都是用埃氏篩
算術基本定理
任何一個大於\(1\)的正整數都能被唯一分解為有限個質數的乘積的形式,寫作:
其中\(c_i\in\N^+\),\(p_i\)都是質數,且滿足\(p_1<p_2<...<p_m\)
試除法分解質因數
就是結合一下埃氏篩和試除法
void divide(int n)
{
cnt=0;
for(int i=2;i*i<=n;++i)
{
if(n%i==0)
{
p[++cnt]=i;c[cnt]=0;
while(n%i==0) n/=i,c[cnt]++;
}
}
if(n>1) p[++cnt]=n,c[cnt]=1;
}
復雜度為\(O( \sqrt N )\)
約數
算術基本定理的推論
如果一個正整數被分解成了\(N=p_1^{c_1}p_2^{c_2}...p_m^{c_m}\)這種形式,則\(N\)的正約數集合可以寫作
\(p_1^{b_1}p_2^{b_2}...p_m^{b_m}\)其中\(0\le b_i \le c_i\)
約數個數
\(N\)的正約數個數為\(\prod\limits_{i=1}^m(c_i+1)\)
約數和
\(N\)的所有正約數和為\(\prod\limits_{i=1}^m(\sum\limits_{j=0}^{c_i}(p_i)^j)\)
求\(N\)的正約數集合
試除法
核心就是利用約數總是成對出現(除了對於完全平方數,\(\sqrt{N}\)會單獨出現)
for(int i=1;i*i<=N;++i)
{
if(n%i==0)
{
d[++cnt]=i;
if(i!=n/i) d[++cnt]=n/i;
}
}
復雜度為\(O( \sqrt N)\)
試除法的推論
一個正整數的約數個數上界為\(2 \sqrt N\)
倍數法
求\(1\backsim N\)中每個數的正約數集合
核心思路就是對於每個約數\(d\),以它為約數的正整數為\(d,2d,3d...\left\lfloor\dfrac{N}{d}\right\rfloor*d\)
for(int i=1;i<=n;++i)
for(int j=1;j<=n/i;++j)
f[i*j].push_back(i);
復雜度為\(O(N+\frac{N}{2}+\frac{N}{3}...+\frac{N}{N})=O(N\log N)\)
倍數法推論
\(1\backsim N\)中每個數的約數個數的總和大約為\(N \log N\)
最大公約數
定理
\(\forall a,b\in \N \ \ \ \gcd(a,b)*lcm(a,b)=a*b\)
更相減損法
\(\forall a,b\in \N,a\ge b \ \ \ \ \gcd(a,b)=\gcd(b,a-b)=\gcd(a,a-b)\)
\(\forall a,b\in \N \ \ \ \gcd(2a,2b)=\gcd(a,b)\)
如果需要高精度計算時,高精度除法(取模)很惡心人,可以用更相減損法來替代歐幾里得算法
歐幾里得法
\(\forall a,b\in \N \ \ \ \gcd(a,b)=\gcd(b,a\%b)\)
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b,a%b)
}
互質與歐拉函數
歐拉函數
定義
\(1\backsim N\)中與\(N\)互質的數的個數,記為\(\varphi(N)\)
$\varphi(N)=N*\prod\limits_{質數p|N}(1-\frac{1}{p}) $
於2020.11.6修改錯誤/kk
積性函數
如果\(a,b\)互質時,有\(f(ab)=f(a)*f(b)\),那么函數\(f\)就是積性函數
積性函數的性質
若\(f\)是積性函數,且在算術基本定理中\(n=\prod_{i=1}^mp_i^{c_i}\)則\(f(n)=\prod_{i=1}^{m}f(p_i^{c_i})\)
歐拉函數基本性質
- \(\forall\ n>1,1\backsim n\)中與\(n\)互質的數的和為\(n*\varphi(n)/2\)
- 若\(a,b\)互質,則\(\varphi(ab)=\varphi(a)*\varphi(b)\)(積性函數的性質)
- 設\(p\)是質數,若\(p\mid n\)且\(p^2\mid n\),則\(\varphi(n)=\varphi(n/p)*p\)
- 設\(p\)是質數,若\(p\mid n\)但是\(p^2 \nmid n\)則\(\varphi(n)=\varphi(n/p)*(p-1)\)
- \(\sum_{d\mid n}\varphi(d)=n\)
試除法求歐拉函數
根據歐拉函數的計算計算式,可以在分解質因數時順便求出歐拉函數
int phi(int n)
{
int ans=n;
for(int i=2;i<sqrt(n);++i)
if(n%i==0)
{
ans=ans/i*(i-1);
while(n%i==0) n/=i;
}
if(n>1) ans=ans/n*(n-1);
return ans;
}
復雜度為\(O(\sqrt N)\)
\(Eratosthenes\)篩法求歐拉函數
根據歐拉函數的計算式,可以在\(O(n \log n)\)的時間內求出\(2 \backsim n\)中每個數的歐拉函數
void euler(int n)
{
for(int i=2;i<=n;++i) phi[i]=i;
for(int i=2;i<=n;++i)
if(phi[i]==i)
for(int j=i;j<=n;j+=i)
phi[j]=phi[j]/i*(i-1);
}
線性篩快速遞推歐拉函數
- 設\(p\)是質數,若\(p\mid n\)且\(p^2\mid n\),則\(\varphi(n)=\varphi(n/p)*p\)
- 設\(p\)是質數,若\(p\mid n\)但是\(p^2 \nmid n\)則\(\varphi(n)=\varphi(n/p)*(p-1)\)
利用上述性質,在線性篩每個合數\(n\)被最小質因子\(p\)篩掉的時候通過\(\varphi(n/p)\)遞推到\(\varphi(n)\)
代碼就不寫了,我沒寫過這種方法(我菜)
同余
同余,同余類和剩余系的定義
自己百度吧,懶得寫了\(qwq\)
費馬小定理
若\(p\)為質數,則對於任意整數\(a\),都有\(a^p \equiv a (\mod p)\)
歐拉定理
若正整數\(a,n\)互質,則\(a^{\varphi(n)} \equiv 1(\mod n)\)
歐拉定理的推論
若正整數\(a,n\)互質,則\(a^n \equiv a^{n\% \varphi(n)}(\mod n)\)
若正整數\(a,n\)不一定互質時,且\(b> \varphi (n)\),\(a^b \equiv a^{b\%\varphi(n)+\varphi(n)}(\mod n)\)
可用於歐拉降冪
裴蜀定理
對於任意整數\(a,b\),都存在一對整數\(x,y\),滿足\(ax+by=\gcd(a,b)\)
擴展歐幾里得算法
int exgcd(int a,int b,int &x,int &y)
{
if(b==0) {x=1;y=0;return a;}
int d=exgcd(b,a%b,x,y);
int z=x;x=y;y=z-y*(a/b)
return d;
}
注意:
上述程序求出方程\(ax+by=\gcd(a,b)\)的一組特解\(x_0,y_0\)
對於更為一般的方程\(ax+by=c\)而言,當\(d\mid c\)時方程有解,此時方程的解為\((c/d)*x_0,(c/d)*y_0\)
方程\(ax+by=c\)的通解可以表示為:
\(x=\frac{c}{d}x_0+k\frac{b}{d}\)
\(y=\frac{c}{d}y_0-k\frac{a}{d}\)
通過上式就可以求出最小正整數解了,\(qwq\)
乘法逆元的計算和應用
大概就是一個整數,能讓\(a/b\)在模\(p\)的意義下等於\(a*inv(b)\)
然后就可以到處取模了,\(qwq\)
逆元的計算
根據費馬小定理求解
對於\(a/b(\mod p)\)而言
若\(p\)為質數,則根據費馬小定理,\(b^{p-1} \equiv 1(\mod p)\)
即\(b*b^{p-2}\equiv 1(\mod p)\)
\(b^{p-2}\)即為\(b\)的乘法逆元
求解同余方程
如果只保證\(b,p\)互質,則可以通過直接求解同余方程\(b*x\equiv 1(\mod p)\)
線性遞推逆元
利用階乘遞推逆元
由於\(a=\frac{a!}{(a-1)!}\)
\(a^{-1}\equiv(a-1)!a!^{-1}\)
\((a-1)!\equiv a!^{-1}a\)
可以求出\(1...k\)的階乘,然后利用快速冪求出\(k!\)的逆元,然后反推\(1...k\)的階乘的逆元
該方法也可以求出任意\(k\)個數的階乘,即把階乘換成前綴和