前置知識
完全剩余系
百度百科:
從模n的每個剩余類中各取一個數,得到一個由n個數組成的集合,叫做模n的一個完全剩余系。
簡單點說,n的完全剩余系就是0到n-1的集合。
縮剩余系
又叫簡化剩余系。
簡單點說,n的縮剩余系就是其完全剩余系中與n互質的數組成的一個集合。
費馬小定理
內容:
證明:
考慮p的縮剩余系,因為p是質數,所以p的縮剩余系為 \(\{1,2,3,\cdots,p-1\}\)
把縮剩余系中的每個數乘上一個數k(要求:\(\gcd(k,p)=1\) ),所得到的集合在模p意義下仍是p的縮剩余系。
上一句如何證明?
只需證明在新集合中任意兩個數都不相等即可。
反證:取出兩個數 \(k\times a_1,k\times a_2\),若這兩個數在模p移一下相等,則
移項得:
即:
又因為
所以得出
而
所以
不成立。
所以所得到的集合在模p意義下仍是p的縮剩余系。
這時候就有下面這個式子:
化簡一下可得:
因為
所以兩邊約去后得
證畢。
應用
求逆元
而逆元應用非常廣泛。
歐拉函數
定義
歐拉函數\(\psi(x)\)表示小於x的數字中與x互質的數的個數。
公式
其中n表示x的質因數個數。
證明
考慮容斥原理。
設 \(p_1,p_2\) 為 \(x\) 的兩個質因數,則 \(p_1\)的倍數有 \(\frac{x}{p_1}\) 個,\(p_2\) 的倍數有 \(\frac{x}{p_2}\) 個。
把這些數刪去,還剩下 \(x-\frac{x}{p_1}-\frac{x}{p_2}+\frac{x}{p_1\times p_2}\) 個。(多刪了 \(\frac{x}{p_1\times p_2}\) 個)
然后就是化簡變形:
推廣到n個質因數,得到公式:
性質
積性函數。
滿足:
若p是質數,則:
線性求代碼實現
線性篩可是很NB的,以至於所有積性函數都怕他(掩蓋不了看似大的驚人的復雜度了)。
————某谷題解
也就是說,所有線性求積性函數的關鍵就是用最小的質因子求。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=100005;
int phi[maxn],cnt,prime[maxn],vis[maxn],n;
int main(){
cin>>n;
phi[1]=1;//初始化
for(int i=2;i<=n;i++){
if(!vis[i]) prime[++cnt]=i,phi[i]=i-1;//篩質數、質數i的歐拉函數值為 i-1
for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
vis[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);
}
}
}
for(int i=1;i<=n;i++) cout<<phi[i]<<" ";
return 0;
}
歐拉定理
內容:
證明:
歐拉定理算是費馬小定理的拓展,所以證明很像。
考慮 \(m\) 的縮剩余系,很顯然縮剩余系中元素個數為 \(\psi(m)\)。
然后把縮剩余系中的每個數乘上一個數 \(a\),要求 \(\gcd(a,m)=1\)。
根據費馬小定理那里證明的命題,新的集合仍是 \(m\) 的縮剩余系。
假設原來的縮剩余系為 \(p\),乘上 \(a\) 以后的縮剩余系為 \(q\),則有:
即:
因為 \(\gcd(\prod_{i=1}^{\psi(m)}p_i,m)=1\),所以兩邊可以同時約掉,得:
證畢。
歐拉降冪公式
又叫做拓展歐拉定理。
有一道板子題:
傳送門
內容:
證明:
略。(挺實用的,一定要背過)
注意當 \(b\ge \psi(m)\) 時才能用公式,否則直接快速冪。
AC代碼
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
long long a,m,mm;
long long phi,res;
int len,yyy;
string s;
long long fp(long long a,long long b){
if(b==1) return a;
long long now=fp(a,b/2);
if(b&1) return now*now%m*a%m;
return now*now%m;
}
int main()
{
cin>>a>>m;
phi=mm=m;
for(int i=2;mm>1&&i<=sqrt(m);i++){
if(!(mm%i)){
phi/=i;
phi*=(i-1);
while(!(mm%i)) mm/=i;
}
}
if(mm>1) phi=phi/mm*(mm-1);
cin>>s;
len=s.length();
for(int i=0;i<len;i++){
res=res*10+s[i]-'0';
if(res>=phi) res%=phi,yyy=1;
}
if(yyy) res=res+phi;
cout<<fp(a%m,res);
return 0;
}