目录
一、基础知识
1、同余定理
定义:
给定一个正整数m,如果两个整数a和b满足a-b能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对模m同余,记作a≡b(mod m)。对模m同余是整数的一个等价关系
性质:
(注意不再是充要条件了)
(下面的特例)
若gcd(c,n)!=1,a≡b(mod n/gcd(c,n) (不会证明)
2、a|b
意思是b可以被a整除
二、素数筛
1、埃式素数筛
先来个基础的过程:
1.把1~n列出来
2.去掉不是特殊的1
3.从小到大,枚举每一个没有删掉的数字i
4.把i的2倍,3倍,4倍,…,删掉
5.剩下的没被删掉的都是素数
const int n=100001;
int book[n];//标记是否删除
memset(book,0,sizeof(book));//初始化,为0代表没删除,为1代表删除
book[1]=1;//1是特例
for(int i=2;i<n;i++)
if(!book[i])
{
for(j=2;j*i<n;j++)
book[j*i]=1;
}//book为0就是素数了
但是这种方法有些数被删除了两次,比如6既被2删除又被3删除,没有必要删那么多次。有一个小优化是把j初始设为i*i,因为i*(i-1),i*(i-2)这些已经被i-1和i-2删除了。但并不能解决所有问题,比如60=2*30=3*20,还是会重复,我们希望每个数只被删一次。
2、欧拉素数筛
#include <stdio.h>
int book[100001],prime[100001];//book说明是否被删,prime保存素数
int main()
{
int n,i,j,top=0;
scanf("%d",&n);
for(i=2;i<=n;i++)
{
if(!book[i])
prime[top++]=i;//没有被删去,那就当素数留下来。
for(j=0;j<top;j++)//注意,无论是否被删除,都进行循环
{
book[prime[j]*i]=1;
if(!(i%prime[j]))//此时素数是i的因子就停下来
break;
}
}
for(i=0;i<top;i++)
printf("%d ",prime[i]);
}
欧拉的主干就是每个数只被自己最小的因子删去。对于这种筛法,最好不要把book[i]理解成删除,还有用,最好理解成留作最大公因子。对于任意合数n,都等于最小公因子与最大公因子之积。对于任意两个不同的数,最大公因子与最小公因子一定不同时相同。每一个删除的数都由它的最小公因子与最大公因子相乘获得,就能保证每次删除的数不同。
当数为i时,把i当作之后某个数的最大公因子。若没被标记,证明是素数,先加到素数组里。此时,素数组肯定都小于等于它,素数组里的都当作最小公因子。相乘开始删,注意到i可以整除此时的素数prime[j]时停止。此时i=x*prime[j],如果往后乘,i*prime[j+1]=x*prime[j+1]*prime[j],对于x*prime[j+1]*prime[j]来说,最大公因子是x*prime[j+1]比i大,最小公因子是primr[j]比prime[j+1]小,所以不能用i*prime[j+1]获得。
这样由于每一次删除数时最大或最小公因子总有一个不同,一定删除的不同。
三、快速幂
1、分解质因数
算术基本定理:任何一个大于1的自然数N,如果N不是素数,那么N可以唯一地表示为
方法一:从2到n反复除以能除的数 O(n)
t=0;//因子的个数
for(int i=2;i<n;i++)
{
if(!n%i)
{
temp[++t]=i;//第t个因子的值
count[t]=n/i;//次方
}
}//n是质数在下面的样程里写了
方法二:素数筛预处理最小素因子,循环除以最小素因子 O(n)+O(logn)
count=t=0;
for(i=0;i<pn&&prime[i]<=n;i++)//pn是小于n的质数的个数
{
if(!n%prime[i])
{
temp[++t]=prime[i];
count[t]=n/prime[i];
}
}
if(temp[t])//n是质数
{
temp[t++]=1;
temp[t]=n;
count[t]=1;
}
2、快速幂(一般都是对m取余)
其实快速幂就是a*a得到,那么之后就可以*=......不用a*a*a*a了
所以就从开始向下分解,n为偶数mod m=mod m;n为奇数mod m=(mod m)*(a mod m)mod m。
一直向下分解到a的0次方返回1。
递推法
long long quick(int n)
{
if(n==0)
return 1;
int half=quick(n/2);
if(n%2)
return (half*half%m)*(n%m);
else
return half*half%m;
}
非递归版
long long quick()
{
res=1;
while(n)
{
if(n%2)
res=res*a%m;
a=a*a%m;
n/=2;
}
return res;
}
遗留问题
1、矩阵快速幂
2、FFT优化
3、高精度乘法优化
四、欧几里得算法
(#include <algorithm>里面有__gcd函数(是双下划线,而且有复杂度))
1、辗转相除法
gcd(1350,211)为例
证明:
充分性:
a可以表示成a = kb + r(a,b,k,r皆为正整数,且r<b),则r = a mod b
假设d是a,b的一个公约数,记作d|a,d|b,即a和b都可以被d整除。
而r = a - kb,两边同时除以d,r/d=a/d-kb/d=m,由等式右边可知m为整数,因此d|r
因此d也是b,a mod b的公约数
必要性:
假设d是b,a mod b的公约数, 则d|b,d|(a-k*b),k是一个整数。
进而d|a.因此d也是a,b的公约数
因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证。
long long gcd(a,b)
{
if(!b)
return a;
return gcd(b,b%a);
}
最大公约数有结合律
代码实现一串数字的最大公约数
res=gcd(a[0],a[1]);
for(int i=2;i<n;i++)
res=gcd(res,a[i]);
最小公倍数
lcm=(a/gcd(a,b))*b;//先除后乘,防止溢出
证明:
a=n*gcd(a,b);
b=m*gcd(a,b);
n,m互质,所以最小公倍数为a*b*gcd(a,b);
2、扩展欧几里得
对于不完全为 0 的非负整数a、b,必然存在整数对x、y,使得gcd(a,b)=ax+by。
求解 x,y的方法的理解
设 a>b。
1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2,a>b>0 时,设
ax1+ by1= gcd(a,b);
bx2+ (a mod b)y2= gcd(b,a mod b);
根据朴素的欧几里得原理有 gcd(a,b) = gcd(b,a mod b);
则: ax1+ by1= bx2+ (a mod b)y2;
即: ax1+ by1= bx2+ (a - [a / b] * b)y2=ay2+ bx2- [a / b] * by2;
([a/b]代表取小于a/b的最大整数)
也就是 ax1+ by1 == ay2+ b(x2- [a / b] *y2);
x1=y2;
y1=x2- [a / b] *y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。
int x,y;
void exgcd(int a,int b,int x,int y)//不用管x,y的值,后面会赋值往前推,前面的x,y值无意义
{
if(!b)
{
x=1;
y=0;
return ;
}
exgcd(b,a%b,x,y);//这一步执行完x,y就被附上有意义的值 b*x+a%b*y=gcd(b,a%b)
int t=x;
x=y;
y=t-a/b*y;//完成x,y的更新 a*x+b*y=gcd(a,b)
}
应用
1、求解线性方程
int solve()
{
if(b%gcd(a,n))
return -1;//如果b不是gcd(a,n)的倍数,无整数解
int x,y;
exgcd(a,n,x,y);
return x*b/gcd(a,n);
}
2、求逆元
等于1的特殊情况。
五、中国剩余定理
1、孙子算经
有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?即,一个整数除以三余二,除以五余三,除以七余二,求这个整数
凡三三数之剩一,则置七十;五五数之剩一,则置二十一;七七数之剩一,则置十五.一百六以上,以一百五减之,即得
2、中国剩余定理
以例题为例。
1、N是3,5,7的公倍数105。
2、N1=35,N2=21,N3=15。
3、x=a1*N1+a2*N2+a3*N3时(此时a1,a2,a3还是未知的),N2、N3都是3的倍数,x mod 3=a1*N1 mod 3=2,同理可得x mod 5=a2*N2 mod 5=3,x mod 7=a3*N3 mod 7=2。
4、更直观一点,代入35,21,15
35*a12(mod 3)
21*a23(mod 5)
15*a32(mod 7)
用扩展欧几里得求得a1,a2,a3
求出x=a1*N1+a2*N2+a3*N3
5、但此时x可能很大,不停减去公倍数105,缩小范围,也保证缩小后的数一定成立,就是x%=105了。
多次减去105不影响数满足初始条件,(x-105)mod n=x mod n +105 mod n=x mod n(这个等式不太严谨,理解就行)