数论

家住魔仙堡 提交于 2019-11-27 10:45:57

基础数论

声明: 参考课件来自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);
        }
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!