基础数论
声明: 参考课件来自PoPoQQQ(虽然我不认识他),讲授来自Accelerator
整理就是我这个juruo做的
基础知识
gcd和lcm
gcd(x,y),简记为(x,y),表示两个数的最大公约数
lcm(x,y),简记为[x,y],表示两个数的最小公倍数
设x=∏pi^ai,y=∏pi^bi
那么(x,y)=∏pi^min(ai,bi),[x,y]=∏pi^max(ai,bi)
由于min(ai,bi)+max(ai,bi)=ai+bi,故有(x,y)[x,y]=x y
同余公式
(a+b)%p = (a%p + b%p) %p
(a-b)%p = (a%p - b%p) %p
(ab)%p = (int)( (LL) (a%p) (b%p) % p )
除法怎么办???
看下面
乘法逆元
定义:对于任意x∈[1,p),若(x,p)=1,则存在唯一的正整数inv[x]∈[1,p)满足x*inv[x]≡1(mod p),我们称inv[x]为x在模p意义下的逆元。
a/x=a* 1/x=a*inv[x] (mod p)
这说明,当我们需要除掉x的时候,我们只需要乘上x的逆元就行了。
求乘法逆元
扩展欧几里得(exgcd
用途:求出方程ax+by=gcd(a,b)的一组解,其中a和b已知,x和y未知
模仿欧几里得算法,假设我们得到了一组解x2和y2满足:
b* x2 + (a%b)* y2=gcd(a,b)
即: b* x2+(a-a/b* b)* y2=gcd(a,b)
那么合并已知项,得:
a* y2+b* (x2-a/b* y2)=gcd(a,b)
于是我们有x=y2,y=x2-a/b* y2满足a* x+b* y=gcd(a,b)
求逆元:a* x≡1(mod p)即a* x+p* y=1,用Exgcd求出任意一个x后mod p即可。
int exgcd(int a, int b, int& x, int& y) { if(!b) { x = 1; y = 0; return a; } int d = exgcd(b, a%b, y, x);//此时算的x=y1, y=x1; y -= (a/b)*x;//y应该为 x1-(a/b)*y1 return d; } int main() { int x, y; scanf("%d%d",&a, &b); exgcd(a, b, x, y); if(x<0) x = (x%b)+b;//如果是负的,要先转正(先%在+保证转正 printf("%d",x%b);//得到的是任意一个x, 所以它可能大于b }
直线上的点: 求ax+by=c上有多少整点(x,y)
设g=gcd(a,b),(注:g==0可以特判一下,以下都是g!=0的情况), ax+by=g的一组解是(x0, y0) 。 当c是g的倍数时,有解, 为(x0* c/g, y0* c/g) (自己化一下);反之,无解。 (听老师说这个叫裴蜀定理...好像是吧)
欧拉定理
欧拉函数:
定义:欧拉函数φ(n)表示[1,n]范围内与n互质的数的个数。
φ(1)=1
φ(p)=p-1 (p为质数, 即费马小定理
φ(p^k)=(p-1)*p^(k-1)
设n=Πpi^ai,那么φ(n)=Π (pi-1)* pi^(ai-1)=n* Π(pi-1)/pi
(我只记了个φ(n) = Π (1 - 1/pi) ....)
例:φ(50)=φ(225)=50(1/2)*(4/5)=20
求欧拉函数:分解质因数 O(√n)
性质:积性函数
欧拉定理
对于任意正整数a,m满足(a,m)=1 (即a,m 互质),有:
a^φ(m)≡1(mod m)
特别地,若m为质数则为费马小定理:
a^(p-1)≡1(mod p)
推论:a*a^(φ(m)-1)≡1(mod m)
回顾乘法逆元的定义,a^(φ(m)-1)即为a在模m意义下的逆元。
//用一个快速幂
线性求逆元
当p是质数的时候我们可以线性预处理出[1,n]之间所有数在模p意义下的逆元
设p=kx+b,那么有:
kx+b≡0 (mod p)
b* inv[b]* inv[x]≡-kx* inv[b]* inv[x] (mod p)
inv[x]≡(p-k)inv[b] (mod p) //转正
inv[x]≡-k*inv[b] (mod p)
故有inv[x]=(p-p/x)*inv[p%x]%p
而p%x<x,因此我们从1~n循环就可以求出1~n中所有数关于p的逆元
逆元求法的使用场合
一般来说如果模数是素数并且需要用到1~n所有数的逆元的时候线性预处理没的说
其他情况一般视p是否是素数而定
如果p是素数,一般用欧拉定理,一个快速幂搞定
如果p不是素数,欧拉定理求φ(p)就比较慢了,一般用Exgcd
积性函数
积性函数可以通过以下两种方式快速计算
1、分解质因数
2、线性筛法
分解质因数:F(n)=F(Πpi^ai)=ΠF(pi^ai)
F(p^a)通常比较好求
那么我们就可以在O(√n)的时间复杂度求出F(n)
下面是线性筛法的做法
线性筛法
正题:
用途:在O(n)的时间内筛出[1,n]范围内所有的素数,顺便筛出[1,n]范围内所有数的φ值/μ值/某积性函数值
方法如下:
枚举i=2..n,如果i没有被标记为“不是素数”就把i加入数组
无论i是不是素数,枚举已经筛到的每一个素数p,将i*p标记为“不是素数”
如果i是p的倍数,退出循环
这样可以保证一个数只会被最小素因子筛到一次,因此复杂度为O(n)
如果要求函数值就顺便讨论讨论计算一下值就行了
补一个: 简单的筛法(E什么什么筛) (这个并不能顺便筛出什么什么积型函数的数值)
// for(int i = 2; i <= n; i++) // for(int j = i*i; j <= n; j+=i) not_p[j] = 1; int tmp = sqrt(n+0.5);//说是解决精度问题,不懂... for(int i = 2; i <= tmp; i++) if(!not_p[i]) //i*i<n //1看题目考虑 for(int j = i*i; j <= n; j+=i) {//没有必要从j*2, 因为在i=2的时候已经被筛掉了 not_p[j] = 1; }
以欧拉函数为例讲解一下线性筛法的实现
void L_S() { phi[1] = 1; for(int i = 2; i <= n; i++) { if(!not_prime[i]) { prime[++tot] = i; phi[i] = i-1; } for(int j = 1; i*prime[j] <= n; j++) { not_prime[i*prime[j]] = 1; if(i % prime[j]) { phi[i*prime[j]] = phi[i]*phi[prime[j]]; break; } phi[i*prime[j]] = phi[i]*(prime[j]-1); } } }