【數論】數論相關口胡
正經人誰學數論啊
持續更新。
因為本人過於蒟蒻所以如果你想在這裡學些什麼的話還是算了
基本也就我自己看看
質數
Eratosthenes篩素數
其實就是劣質版線性篩,不過程式碼比較短,打個小表還是可以的。
時間效率\(O(nlog\ log\ n)\)
int Pri[maxn]; bool NotPri[maxn]; void JudPri(int n){ NotPri[0]=1; NotPri[1]=1;//全域性變數大括號賦值可是很不好的習慣哦... for(int i=2;i<=n;i++){ if(NotPri[i])continue; Pri[++Pri[0]]=i; for(int j=i;(long long)i*j<=n;j++) NotPri[i*j]=1; } }
線性篩素數
普通版
int Pri[maxn];
bool NotPri[maxn]={1,1};
void JudPri(int n){
NotPri[0]=1;
NotPri[1]=1;
for(int i=2;i<=n;i++){
if(!NotPri[i]){
Pri[++Pri[0]]=i;
}
for(int j=1;j<=Pri[0]&&i*Pri[j]<=n;j++){
NotPri[i*Pri[j]]=1;
if(i%Pri[j]==0)break;
}
}
}
無需取模版
int v[maxn],Pri[maxn]; void JudPri(int n){ for(int i=2;i<=n;i++){ if(v[i]==0){ v[i]=i; Pri[++Pri[0]]=i; } for(int j=1;j<=Pri[0];j++){ if(i*Pri[j]>n||Pri[j]>v[i])break; v[i*Pri[j]]=Pri[j]; } } }
可能玄學上更快?(大霧)不過可能普通版的bool
陣列更快也說不定呢(好像真的會快很多)。
Miller_Robbin大素數判定
(所以名字到底有幾個b?)
深入學習建議看Gary_818的部落格。
是看臉的隨機化演算法,錯誤率基本趨近於0。
0202年了不會還有人\(O(\sqrt n)\)判斷素數吧不會吧不會吧
這種判斷素數的方法利用的是費馬小定理的逆命題,隨機列舉一個\(a\),滿足這個同餘式,那麼\(p\)就是素數。
不過其逆命題並不是個真命題(大霧),例如在\(p=341\)的時候若\(a=2\)滿足費馬小定理,然而341是一個合數(\(341=11\times 31\))。因此僅判斷一次得到的結果不一定正確,那怎麼增大正確率呢?
那就是重複判斷30次
時間複雜度\(O(log\ n)\)(忽略常數)
#include<bits/stdc++.h>
using namespace std;
const int count=30;
int n;
inline int qpow(int a,int b,int Mod){
int ans=1,base=a;
while(b){
if(b&1)ans=ans*base%Mod;
base=base*base%Mod;
b>>=1;
}
return ans;
}
bool Miller_Rabbin(int n){
if(n==2)
return true;
for(int i=1;i<=count;i++){
int a=rand()%(n-2)+2;
if(qpow(a,n,n)!=a)
return false;
}
return true;
}
int main(){
srand(time(0));
scanf("%d",&n);
if(Miller_Rabbin(n))
printf("Probably a prime.");
else
printf("A composite.");
printf("\n");
return 0;
}
約數
GCD
輾轉相除法求GCD
各位dalao已經倒著都能寫了。
輾轉相除法求最大公約數。或者為了防止爆棧可以改成迴圈。
inline int gcd(int x,int y){
if(y==0)return x;
else return(y,x%y);
}
二進位制方法求GCD
如果想優化(可能大資料可以優化幾百\(\mathrm{ms}\))
就變成魔法少女就可以用二進位制優化(不過平時基本也無用)
- \(a\),\(b\)為偶數,則\(\gcd(a,b)=2\times \gcd(a/2,b/2)\)。
- \(a\)為奇數,\(b\)為偶數,則\(\gcd(a,b)=\gcd(a,b/2)\)。
- \(a\),\(b\)為奇數。假設\(a\geq b\),則\(\gcd(a,b)=\gcd((a-b)/2,b)\)。
- \(a\)為\(0\),則返回\(b\)。
可以用手寫abs和min卡常
inline ll abs(ll x){
return x<0?-x:x;
}
inline ll min(ll a,ll b){
return a<b?a:b;
}
inline ll gcd(ll a,ll b){
if(a==0)return b;
if(b==0)return a;
if(!(a&1)&&!(b&1))return 2*gcd(a>>1,b>>1);
else if(!(a&1))return gcd(a>>1,b);
else if(!(b&1))return gcd(a,b>>1);
else return gcd(abs(a-b),min(a,b));
}
沒有取模操作會快很多。
更相減損術求GCD
懶得寫,建議BFS。
高精度運算的時候可以用(不過既然都是要用高精度的題了為何不棄了呢)
算數基本定理的推論
算數基本定理(唯一分解定理)
任意大於1的正整數\(N\)都可分解為有限個素數的乘積。
\(N=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m}\)
其中\(c_i\)都是正整數,\(p_i\)都是素數且滿足\(p_1<p_2<p_3⋯<p_m\)。
求正約數個數
\(N\)的正約數個數為\(\prod\limits^m_{i=1}(c_i+1)\)
約數和定理
\(N\)的所有正約數之和為\(\prod\limits^m_{i=1}(\sum\limits^{c_i}_{j=0}(p_i)^j)\)
求正約數集合
一個大於1的正整數\(N\)的正約數集合可寫作
\[\{p_1^{b_1}p_2^{b_2}\dots p_m^{b_m}\},0\leq b_i\leq c_i \]
試除法
時間效率\(O(\sqrt n)\)。不會有人不會吧不會吧不會吧。
倍數法
倍數法以\(O(nlog\ n)\)的效率求出\(1-n\)所有數的正約數集合,比試除法的\(O(n\sqrt n)\)快很多。
vector<int> fac[maxn];
for(int i=1;i<=n;i++)
for(int j=1;i*j<=n;j++)
fac[i*j].push_back(i);
尤拉函式
\(1-N\)中與\(N\)互質的數的個數成為尤拉函式。
\[\varphi(n)=n \times \prod_{i=1}^k (1-\frac{1}{p_i}),p_i|n \]
性質
建議去看虎哥部落格(懶得粘)
分解質因數求單個尤拉函式
這不是有手就行嗎
Eratosthenes篩求尤拉函式
妹想到吧又事它,時間效率\(O(nlog\ n)\),雖然效率不高但是很短(?)
int phi[maxn];
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);
}
線性篩求尤拉函式
這不是有手就行嗎
擴充套件歐幾里得
深入學習建議看YouXam的部落格。
擴充套件歐幾里德定理(Extended Euclidean algorithm, EX(噁心)GCD)。
用於求\(ax+by=\gcd(a,b)\)的一組整數解。
\[ax_1+by_1=\gcd(a,b) \]
\[bx_2+(a\%b)y_2=gcd(b,a\%b) \]
\[x_1=y_2, y_1=x_2-\lfloor\frac{a}{b}\rfloor y_2 \]
int exgcd(int a,int b,int &x,int &y) {
if (b==0){
x=1;
y=0;
return a;//a為最大公約數
}
int ret=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
return ret;
}
設\(\gcd(a,b)=d\),對於更一般的方程\(ax+by=c\),它有解當且僅當\(d|c\)。可以先求出\(ax+by=d\)的一組特解\(x_0,y_0\),使其同時乘上\(\frac{c}{d}\),就得到了\(ax+by=c\)的一組特解\(\frac{c}{d}x_0,\frac{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}\ (k\in\mathrm{Z}) \]
同餘
費馬小定理
不是小費馬定理
前置芝士
剩餘類&剩餘系:總之大概就是一些數\(mod\)一個正整數\(n\)之後都能得到什麼結果(敷衍),最常用的完全剩餘系\(\{0,1,…,n-1\}\)。
正文
若\(p\)為素數,且\(a\)不是\(p\)的倍數則:
\[a^{p-1}\equiv 1(mod\ p) \]
另一個形式:若\(p\)為素數,對於任意整數\(a\):
\[a^p\equiv a(mod\ p) \]
證明:不會。建議記住(霧)。
尤拉定理
當模數為合數的時候需要用範圍更廣泛的尤拉定理。
若\(gcd(a,m)=1\),則:
\[a^{\varphi(m)}\equiv 1\ (mod\ m) \]
擴充套件尤拉定理
\[a^b\equiv \begin{cases} a^{b\ mod\ \varphi(p)}, & \gcd(a,p)=1 \\ a^b, & \gcd(a,p)\neq 1,b<\varphi(p)\pmod p \\ a^{b\ mod\ \varphi(p)+\varphi(p)}, & \gcd(a,p)\neq 1,b\geq\varphi(p) \end{cases}\]
證明
(好像沒用過)
乘法逆元
眾所周知同餘不滿足同除性,所以數學家們弄出了逆元這個東西。
費馬小定理求逆元
即\(a^{p-2}\ mod\ p\)。快速冪求解,很方便。
前提條件:模數為素數,且\(a\)不是\(p\)的倍數。有的題特意卡這個,例如沙拉公主的困惑。
尤拉定理求逆元
即\(a^{\varphi(p)-1}\ mod\ p\)。
擴充套件歐幾里得求逆元
前提條件:\(a\)和\(p\)互質。
#include<bits/stdc++.h>
using namespace std;
int b,x,y,mod;
inline int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;
y=0;
return a;
}
int ret=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-(a/b)*y;
return ret;
}
int main(){
scanf("%d%d",&b,&mod);
int gcd=exgcd(b,mod,x,y);
if(gcd!=1)
printf("None\n");
else
printf("%d\n",(x%mod+mod)%mod);
return 0;
}
線性求逆元
建議背式子
\[i^{-1} \equiv -\lfloor \frac{p}{i} \rfloor \times (p\ mod\ i)^{-1} \ (mod\ p) \]
inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=(p-p/i)*inv[p%i]%p;
線性同餘方程
(蒟蒻表示並不會高次的)
暫時懶得寫了,下午填坑
中國剩餘定理
咕咕咕
莫比烏斯反演&&拉格朗日插值
建議問\(liuchanglc\)(滑稽)
震驚,\(\LaTeX\)竟如此難打!活到爆!