数论笔记1

质数

1、质数的判定:

试除法判定质数

试除法判定质数

//朴素写法,时间复杂度O(n)
bool isprime(int n){
    
    if(n<2) return false;
    
    for(int i = 2; i < n; ++ i)
        if(n % i == 0)
            return false;
    return true;
}

我们发现对于任何一个能整除n的数d,则n / d也能整除n
所以我们只需要枚举到n / d即可

//优化后代码,时间复杂度是O(根号n)
bool isprime(int n){
    
    if(n<2) return false;
    
    for(int i = 2; i <= n / i; ++ i)
        if(n % i == 0)
            return false;
    return true;
}

2、分解质因数

分解质因数

//试除法分解质因数
void divide(int n){
    
    for(int i = 2; i <= n; ++ i)
        if(n % i == 0){
            int s = 0;
            while(n % i == 0){
                n /= i;
                s ++;
            }
            cout << i << ' ' << s << endl;
        }
    puts("");
}

我们发现n中至多有一个质因数大于根号n,假设有两个质因数x、y大于根号n,那么x * y就大于n不符合条件,所以我们将这个大于根号n的质因数特判一下即可

void divide(int n){
    
    for(int i = 2; i <= n / i; ++ i)
        if(n % i == 0){
            int s = 0;
            while(n % i == 0){
                n /= i;
                s ++;
            }
            cout << i << ' ' << s << endl;
        }
    if(n > 1) cout << n << ' ' << 1 << endl;
    puts("");
}

线性筛

筛法求质数

筛质数

朴素筛法:从小到大枚举,未被标记则加入质数表内,然后把它的倍数筛除

//时间复杂度O(nlogn)
void get_primes(int n)
{
    for(int i = 2; i <= n; ++ i){
        if(!st[i]) primes[cnt ++] = i;
        
        for(int j = i + i; j <= n; j += i)
            st[j] = true;
    }
}

我们发现有很多冗余,有些数被筛除了很多次,观察质数和约数的性质,我们发现每一个约数都是质数的倍数,所以我们只用筛除质数的倍数即可

//时间复杂度为O(nloglogn)
void get_primes(int n)
{
    for(int i = 2; i <= n; ++ i){
        if(!st[i]){
            primes[cnt ++] = i;
        
            for(int j = i + i; j <= n; j += i)
                st[j] = true;
        }
    }
}

线性筛法:每次只用一个数的最小质因数将这个数筛除

//时间复杂度O(n)
void get_primes(int n)
{
    for(int i = 2; i <= n; ++ i){
        if(!st[i]) primes[cnt ++] = i;
        
        for(int j = 0; primes[j] <= n / i; ++ j){
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) break;
        }
    }
}

在这里插入图片描述

线性筛约数个数

在这里插入图片描述

线性筛约数之和

在这里插入图片描述

约数

试除法求约数

试除法求约数

和试除法判定质数相同,我们发现对于任何一个能整除n的数d,则n / d也能整除n,所以我们只用枚举到n / d,用d算出来另一个约数n / d即可

//时间复杂度O(根号n + nlogn),排序时间复杂度O(nlogn)
vector<int> get_divisors(int n){
    
    vector<int> res;
    
    for(int i=1; i<=n/i; ++i)
        if(n%i == 0){
            res.push_back(i);
            if(i != n/i) res.push_back(n/i);
        }
    sort(res.begin(), res.end());
    return res;
}

约数个数与约数之和

约数个数

约数之和

在这里插入图片描述

最大公约数

欧几里德算法

int gcd(int a, int b)
{
    return b ? gcd(b, a%b) : a;
}

在这里插入图片描述

欧拉函数

欧拉函数

筛法求欧拉函数

欧拉函数及证明

在这里插入图片描述
在这里插入图片描述

欧拉函数的七个性质

在这里插入图片描述

筛法求欧拉函数

在这里插入图片描述
具体代码如下

#include<iostream>

using namespace std;

typedef long long LL;

const int N = 1000100;

int n;
int primes[N], cnt;
int phi[N];
bool st[N];

LL get_euler(int n){
    
    phi[1] = 1;
    for(int i = 2; i <= n; ++ i){
        
        if(!st[i]){
            primes[cnt ++] = i;
            phi[i] = i - 1;// i是质数时phi[i] = i - 1
        }
        
        for(int j = 0; primes[j] <= n / i; ++ j){
            st[i * primes[j]] = true;
            if(i % primes[j] == 0){
                phi[primes[j] * i] = primes[j] * phi[i];
                //i % pj == 0时, phi[pj * i] = pj * phi[i]
                break;
            }
            phi[primes[j] * i] = (primes[j] - 1) * phi[i];
            //i % pj != 0时, phi[pj * i] = (pj - 1) * phi[i]
        }
    }
    
    LL res = 0;
    for(int i = 1; i <= n; ++ i) res += phi[i];
    return res;
}

int main(){
    
    cin >> n;
    cout << get_euler(n) << endl;
    
    return 0;
}

欧拉定理及其证明

在这里插入图片描述

费马小定理

在这里插入图片描述

快速幂

快速幂

快速幂求逆元

思想:倍增的思想,和二进制可以组成任何数

代码模板

int qmi(int a, int b, int p){
    
    int res = 1;
    while(b){
        if(b & 1) res = (LL)res * a % p;
        b >>= 1;
        a = (LL)a * a % p;
    }
    return res;
}

乘法逆元的定义

若整数 b,m 互质,并且对于任意的整数 a,如果满足 b|a,则存在一个整数 x,使得 a/b≡a×x(mod m),则称 x 为 b 的模 m 乘法逆元,记为 b^−1 (mod m)。
b 存在乘法逆元的充要条件是 b 与模数 m 互质。当模数 m 为质数时,b^m−2 即为 b 的乘法逆元。

扩展欧几里德算法

扩展欧几里德算法

线性同余方程

裴蜀定理:对于任意正整数a,b,那么一定存在非零整数x、y使得ax + by = gcd(a, b)

在这里插入图片描述
利用扩展欧几里得算法我们可以求出一组通解 x 0 , y 0 x_0,y_0 x0,y0
其通解公式为:
{ x k = x 0 + k × b g c d ( a , b ) y k = y 0 − k × a g c d ( a , b ) k ∈ Z \left\{\begin{matrix} x_k=x_0+k\times \frac{b}{gcd(a,b)} & \\ y_k=y_0-k\times \frac{a}{gcd(a,b)}& \end{matrix}\right. k\in \mathbb{Z} {xk=x0+k×gcd(a,b)byk=y0k×gcd(a,b)akZ

代码模板

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);
    
    y -= a/b * x;
    return d;
}

中国剩余定理

中国剩余定理

在这里插入图片描述

高斯消元

高斯消元解线性方程组

高斯消元解异或线性方程组

在这里插入图片描述

代码模板

int gauss(){
    
    int r, c;
    for(c = 0, r = 0; c < n; ++ c){
        
        int t = r;
        for(int i = r + 1; i < n; ++ i)
            if(fabs(a[i][c]) > fabs(a[t][c]))
                t = i;
        
        if(fabs(a[t][c]) < eps) continue;   
        for(int i = c; i < n + 1; ++ i) swap(a[t][i], a[r][i]);
        for(int i = n; i >= c; -- i) a[r][i] /= a[r][c];
        
        for(int i = r + 1; i < n; ++ i)
            if(fabs(a[i][c]) > eps)
                for(int j = n; j >= c; -- j)
                    a[i][j] -= a[r][j] * a[i][c];
        r ++;
    }
    
    
    if(r < n){
        for(int i = r; i < n; ++ i)
            if(fabs(a[i][n]) > eps)
                return 2;
        return 1;
    }
    
    for(int i = n - 1; i >= 0; -- i)
        for(int j = i + 1; j < n; ++ j)
            a[i][n] -= a[i][j] * a[j][n];
    
    return 0;
}

求组合数

求组合数 I

求组合数 II

求组合数 III

求组合数 IV

满足条件的01序列

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值