取余数(remainder)
给定一个正整数,对任意一个整数,一定存在等式,其中和是正整数,且,记作
除法?
要用到逆元的概念,后面会介绍
:是的因数,
:与对模同余,
给定,求
朴素算法
xxxxxxxxxx
int ans=1;
for (int i=1;i<=n;i++){
ans=ans*a%p
}
复杂度
快速幂
xxxxxxxxxx
int quick_power(int a,int n,int p){
if (n==0) return 1;
if (n%2){
int tmp=quick_power(a,n/2,p);
return tmp*tmp%p;
}else{
int tmp=quick_power(a,(n-1)/2,p);
return tmp*tmp%p*a%p;
}
}
//非递归写法:
int qpow(int a,int b,int p){
int t=1,y=a;
while (b>0){
if (b&1) t=t*y%p%p;
y=y*y%p%p;
b>>=1;
}
return t;
}
矩阵快速幂
矩阵乘法
xxxxxxxxxx
a[p][q],b[q][r],c[p][r];
for (int i=1;i<=p;i++)
for (int j=1;j<=r;j++)
for (int k=1;k<=q;k++)
c[i][j]+=a[i][k]*b[k][j]
递推式与矩阵
对于斐波那契数列
我们可以构造矩阵
然后用快速幂求,就可以用的复杂度求斐波那契的第n项
:和里面共有的因数中最大的那个
与互质:
对于任意的正整数,有,所以任意正整数都是的因数
证明:
当时,,
如果,则有
(1)
(2)
由得到,则与有公因数,与矛盾
设,则,,且与互质
- 若,则
- 否则,
证明:
当时,,其中,是正整数
如果,则
(1)
(2)
由得到,则与有公因数,与矛盾
设,则,,且与互质
若,则
否则,设,则存在正整数,使得
辗转相除法(欧几里得算法):
xxxxxxxxxx
int gcd(int a,int b){
if (b == 0) return a;
else return gcd(b,a%b);
}
//写成一行:
int gcd(int a,int b){ return b == 0 ? a : gcd(b,a%b) }
时间复杂度:
引理:若,则
若 ,则
若,则
经过算法迭代一次后得到,再一次迭代得到,
每经过两次迭代的值至少减半,即的值指数级减小
求线段上的整点个数
给两个整点,求线段上的整点个数
设
设直线上的一个整点为
则
则
问题转化为有多少个,使得是一个整数
与互质,是的倍数
Pick定理:对于一个顶点都在整点上的多边形,设它的面积为S,它内部的整点个数为a,它的边上的整点个数为b,那么有
对于任意两个整数,,一定存在整数,使得
exgcd可以得到一组
对于任意两个整数,
对于最左边的式子,存在整数,使得
对于最右边的式子,存在整数,使得
原问题“对于,,求解,”转化为子问题“对于,,求解,”
整理得
要使上式对任意,都成立,则
边界:当时,
xxxxxxxxxx
int exgcd(int a, int b, int& x, int& y){
if (b==0){
x = 1;
y = 0;
return a;
}else{
int d=exgcd(b, a%b, y, x);
y = y - a/b*x;
return d;
}
}
给整数,求解
如果,使用exgcd求得一组解,
通解为
如果
通解为
其他情况无解
求乘法逆元
求
求
设
那么
问题转化为:给两个整数,求一个使得,即,这里的就是模意义下的乘法逆元
可以看出当且仅当时,存在
xxxxxxxxxx
int reverse(int a,int p){
int x,y;
if (exgcd(a,p,x,y)==1)
return (x%p+p)%p;//使x落在[0,p-1]内
else
return 0; //逆元不存在
}
解模线性方程组
最小公倍数LCM:
模线性方程组:
也是用来求解模线性方程组的,但是要求模数两两互质
费马小定理:若是一个质数,且整数不是的倍数,则有
费马小定理求乘法逆元:
设为模意义下的乘法逆元,那么
又因为
取
xxxxxxxxxx
int inverse(a,p){
return qpow(a,p-2,p);
}
xxxxxxxxxx
int cnt=0;
bool is_prime[N];
for (int i=2;i<N;i++){
int flag=1;
for (int j=2;j*j<=i;j++){
if (i%j==0){
flag=0;
break;
}
}
is_prime[i]=flag;
}
xxxxxxxxxx
int prime[N];
for (int i=2;i<N;i++) is_prime[i]=1;
for (int i=2;i<N;i++){
if (is_prime[i]) prime[cnt++]=i;
for (int j=2;j*i<N;j++){
is_prime[i*j]=0
}
}
xxxxxxxxxx
for (int i=2;i<N;i++) is_prime[i]=1;
for (int i=2;i<N;i++){
if (is_prime[i])
prime[cnt++]=i;
for (int j=0;j<cnt&&i*prime[j]<N;j++){
is_prime[i*prime[j]]=0;
if (i%prime[j]==0)
break;
}
}