取余数(remainder)
给定一个正整数,对任意一个整数,一定存在等式,其中和是正整数,且,记作
除法?
要用到逆元的概念,后面会介绍
:是的因数,
:与对模同余,
给定,求
朴素算法
xxxxxxxxxxint ans=1;for (int i=1;i<=n;i++){ ans=ans*a%p}复杂度
快速幂
xxxxxxxxxxint 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;}
矩阵快速幂
矩阵乘法
xxxxxxxxxxa[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)
由得到,则与有公因数,与矛盾
设,则,,且与互质
若,则
否则,设,则存在正整数,使得
辗转相除法(欧几里得算法):
xxxxxxxxxxint 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可以得到一组
对于任意两个整数,
对于最左边的式子,存在整数,使得
对于最右边的式子,存在整数,使得
原问题“对于,,求解,”转化为子问题“对于,,求解,”
整理得
要使上式对任意,都成立,则
边界:当时,
xxxxxxxxxxint 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求得一组解,
通解为
如果
通解为
其他情况无解
求乘法逆元
求
求
设
那么
问题转化为:给两个整数,求一个使得,即,这里的就是模意义下的乘法逆元
可以看出当且仅当时,存在
xxxxxxxxxxint 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:
模线性方程组:
也是用来求解模线性方程组的,但是要求模数两两互质
费马小定理:若是一个质数,且整数不是的倍数,则有
费马小定理求乘法逆元:
设为模意义下的乘法逆元,那么
又因为
取
xxxxxxxxxxint inverse(a,p){ return qpow(a,p-2,p);}
xxxxxxxxxxint 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;}xxxxxxxxxxint 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 }}xxxxxxxxxxfor (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; }}