模板庫(二) - 數論演算法模板
阿新 • • 發佈:2018-12-15
寫在前面
“模板庫”這一系列文章用來複習
模板
由於時間原因,作者無法一一親自除錯其中的程式,也因如此,有一部分程式來自於網際網路,如果您覺得這侵犯了您的合法權益,請聯絡
刪除。
對於給您造成的不便和困擾,我表示深深的歉意。
本系列文章僅用於學習,禁止任何人或組織用於商業用途。
本系列文章中,標記*的為選學演算法,在
中較少涉及。
數論演算法模板
快速冪
【簡介】
顧名思義,快速冪就是快速算底數的次冪。其時間複雜度為 ,與樸素的相比效率有了極大的提高。
【程式碼實現】
#include<cstdio>
int b,p,mod;
inline int Fast_pow(long long b,int p){
long long ans=1;
while(p){
if(p&1) ans=ans*b%mod;
b=b*b%mod;p>>=1;
}
return ans;
}
int main(){
scanf("%d%d%d",&b,&p,&mod);
printf("%d^%d mod %d=%d",b,p,mod,Fast_pow(b,p));
return 0;
}
複雜度
歐幾里得演算法
【簡介】
歐幾里德演算法又稱輾轉相除法,是指用於計算兩個正整數的最大公約數。應用領域有數學和計算機兩個方面。計算公式。
【程式碼實現】
#include<cstdio>
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main(){
int a,b;
scanf("%d%d",&a,&b);
printf("%d",gcd(a,b));
}
複雜度
擴充套件歐幾里得演算法
【簡介】
擴充套件歐幾里德演算法是用來在已知求解一組,使它們滿足貝祖等式: (解一定存在,根據數論中的相關定理)。擴充套件歐幾里德常用在求解模線性方程及方程組中。
【程式碼實現】
#include<cstdio>
int exgcd(int a,int b,int &x,int &y){
if(!b)x=1,y=0;
else exgcd(b,a%b,y,x),y-=x*(a/b);
}
int main(){
int a,b,x,y;
scanf("%d%d",&a,&b);
exgcd(a,b,x,y);
printf("%d",(x%b+b)%b);
return 0;
}
複雜度
逆元
【簡介】
乘法逆元,是指數學領域群中任意一個元素,都在中有唯一的逆元,具有性質,其中為該群的單位元。
【程式碼實現】
方法1:求單個逆元
#include<cstdio>
int n,mod;
inline int Fast_pow(long long b,int p){
long long ans=1;
while(p){
if(p&1) ans=ans*b%mod;
b=b*b%mod;p>>=1;
}
return ans;
}
int main(){
scanf("%d%d",&n,&mod);
printf("%d",Fast_pow(n,mod-2);
return 0;
}
複雜度
方法2: 線性求逆元
#include<cstdio>
long long a[100007]={0,1};
int main(){
int n,p;
scanf("%d%d",&n,&p);
for(int i=2;i<=n;i++) a[i]=(p-(p/i))*a[p%i]%p;
for(int i=1;i<=n;i++) printf("%lld\n",a[i]);
return 0;
}
複雜度
篩法求積性函式
【簡介】
篩法是一種簡單檢定素數的演算法。據說是古希臘的埃拉託斯特尼 ,約公元前 年)發明的,又稱埃拉託斯特尼篩法 ,後來擴充套件到利用篩法求積性函式。
【一些性質】
積性函式:對於函式
,若滿足對任意互質的數字
,
,
,且
,那麼稱函式
為積性函式。
狄利克雷卷積:對於函式
,
,定義它們的卷積為
。
兩個積性函式的狄利克雷卷積仍為積性函式。
積性函式都可以用線性篩篩出來
【程式碼實現】
方法1
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int is_not[10000007]={1,1};
void prime(int n){
int m=sqrt(n);
for(int i=2;i<=m;i++)
if(!shu[i])for(int j=i*i;j<=n;j+=i)shu[j]=1;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
prime(N);
for(int i=1;i<=m;i++)scanf("%d",&a),printf(shu[a]?"No\n":"Yes\n");
return 0;
}
複雜度
方法2
#include<cstdio>
bool is_not[10000007]={1,1};
int prime[664579+7],n,m,a,cnt;
void Prime(int n){
for(int i=2;i<=n;++i){
if(!is_not[i])prime[++cnt]=i;
for(int j=1;j<=cnt && prime[j]*i<=n;++j){
is_not[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
int main(){
scanf("%d%d",&n,&m);
Prime(n);
for(int i=1;i<=m;++i)scanf("%d",&a),printf(is_not[a]?"No\n":"Yes\n");
return 0;
}
複雜度
【利用篩法求一些常見的積性函式】
複雜度
莫比烏斯函式
inline void get_mu(int N){
is_not[1]=1;mu[1]=1;
for(int i=2;i<=N;++i){
if(!is_not[i])prime[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt && i*prime[j]<=N;++j){
is_not[i*prime[j]]=1;
if(i%prime[j]) mu[i*prime[j]]=-mu[i];
else{mu[i*prime[j]]=0;break;}
}
}
}
尤拉函式
void getphi(){
phi[1]=1;
for(int i=2;i<=N;++i){
if(!is_not[i])prime[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt,i*prime[j]<=N;++j){
is_not[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);
}
}
}
約數個數
is_not[1