前置知识
完全剩余系
百度百科:
从模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 imes a_1,k imes 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 imes p_2}) 个。(多删了 (frac{x}{p_1 imes 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),所以两边可以同时约掉,得:
证毕。
欧拉降幂公式
又叫做拓展欧拉定理。
有一道板子题:
传送门
内容:
证明:
略。(挺实用的,一定要背过)
注意当 (bge 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;
}