ACM数学基础知识

主要内容

  • 同余
  • 快速幂
  • 快速乘
  • 欧拉函数
  • 欧拉定理
  • 费马定理
  • 乘法逆元
  • 扩展欧几里得算法

同余的性质

快速幂

快速幂用于求解 ,时间复杂度

用二进制表示有 位,令 ,则有:

于是:

其中:

typedef long long ll;
ll quick_mod(ll a, ll n, ll m) {
    if(!n)
        return 1 % m;

    ll res = 1;
    while (n) {
        if(n & 1) {  // 二进制最后一位为 1是奇数
            res = (res * a) % m;
        }
        a = (a * a) % m; 
        n >>= 1;  // 右移一位就是整除 2
    }
    return res;
}

快速乘

快速幂用于求解 ,思想与快速幂一致,时间复杂度

用二进制表示:

其中 .

于是:

其中:

typedef long long ll;
ll quick_mod(ll a, ll b, ll m) {
    ll res = 0;
    while (b) {
        if(b & 1) {  // 二进制最后一位为 1是奇数
            res = (res + a) % m;
        }
        a = (a + a) % m; 
        b >>= 1;  // 右移一位就是整除 2
    }
    return res;
}

质数

算术基本定理

算术基本定理,又称正整数唯一分解定理,即每一个正整数都可以被分解成若干质数的乘积,且分解的方式唯一。

,其中 是质数,

质数的判定 —— 试除法

bool is_prime(int n) {
    for(int i = 2; i <= n / i; ++i) {
        if(n % i == 0) return false;
    }
    return true;
}

约数

求一个数的所有约数 —— 试除法

如果 ,那么 。令 ,则 。总体时间复杂度

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); // 如果 i 是 n 的约数,那么 n / i 也是 n 的约数
        }
    }
    sort(res.begin(), res.end());
    return res;
}

约数个数

约数个数定理

一个大于 的正整数
的正约数的个数为

约数之和

约数和定理

一个大于 的正整数
的正约数的个数为

欧拉函数

定义

中于 互质的个数

例:

求法:

例:

证明:

容斥原理

中和 互质的个数

  1. 中去掉 的倍数。
  1. 加上所有 的倍数 (所有 的倍数被去掉了两次)
  1. 减去所有 的倍数
  1. 重复上述步骤

把公式 展开就是公式

用定义求欧拉函数

时间复杂度

// 用定义求欧拉函数
// O(sqrt(N))
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    while(n--) {
        int a;
        cin >> a;
        int res = a;
        for(int i = 2; i <= a / i; i++) {
            if(a % i == 0) {
                // res = res * (1 - 1 / i);
                res = res / i * (i - 1);
                while(a % i == 0) a /= i;
            }
        }
        if(a > 1) res = res / a * (a - 1);
    }
}

筛法求欧拉函数

中每一个数的欧拉函数

用定义求的时间复杂度

#include <iostream>
#include <algorithm>
using namespace std;

typedef long long ll;

const int N = 1000000 + 10;

int prime[N], cnt;
int phi[N];
bool st[N];

ll get_eulers(int n) {
    for(int i = 2; i <= n; ++i) {
        if(!st[i]) {
            primes[cnt++] = i;
            phi[i] = i - 1;  // 小于质数 p 的所有数都与 p 互质
        }
        for(int j = 0; primes[j] <= n / i; ++j) {
            st[primes[j] * i] = 1;
            if(i % primes[j] == 0) {  // pj 是 i 的质因子
                // phi(pj * i)  phi(i)
                // pj * i 的所有质因子与 i 的所有质因子相同
                // phi(i) = i * (1 - 1 / p1) ... (1 - 1 / pk)
                // phi(pj * i) = pj * i * (1 - 1 / p1) ... (1 - 1 / pk)
                // phi(pj * i) = pj * phi(i)
                phi[primes[j] * i] = primes[j] * phi(i);
                break;
            }
            // i mod pj != 0
            // phi(i) = i * (1 - 1 / p1) ... (1 - 1 / pk)
            // phi(pj * i) = pj * i * (1 - 1 / p1) ... (1 - 1 / pk) * (1 - 1 / pj)
            // phi(pj * i) = phi(i) * pj * (1 - 1 / pj) = phi(i) * (pj - 1)
            phi[primes[j] * i] = phi[i] * (primes[j] - 1)
`        }
    }

    ll res = 0;  // phi(1) = 1
    for(int i = 1; i <= n; ++i) {
        res += phi[i];
    }
    return res;
}

int main() {
    int n;
    cin >> n;

    cout << get_eulers(n) << endl;

    return 0;
}

欧拉定理

互质,则

例:

证明:

中,所有与 互质的数为:

也和 互质,且互不相同。(若相同,设 ,则 ,则 ,矛盾)

中所有与 互质的数,且互不相同。

也为 中所有与 互质的数 (模 ),且互不相同。

两个集合都包含 个与 互质的数,只不过顺序不同,因此两个集合相等。

则有

由于

因此

即可得出

证毕

费马定理

为质数,,则

费马定理是欧拉定理的简单推论

证明:

为质数,则 ,根据欧拉定理即可得证。

乘法逆元

定义

对于某一个 ,能找到一个 ,使得 ,则 称为 的逆元,记作 (不是 的负一次方,是一个整数)。

逆元的性质

问题即转化为:

对于某一个 ,求一个 ,使得 ,其中 是质数。

逆元的求法

由费马定理:

因此,如果 是质数,则 的逆元是 。因此可以用快速幂求逆元。

注意

的不互质时不存在逆元

#include <iostream>
#include <cstdio>
using namespace std;

typedef long long ll;
// a ^ k % p
int qmi(int a, int k, int p) {
    int res = 1;
    while(k) {
        if(k & 1) res = (ll)res * a % p;
        k >>= 1;
        a = (ll)a * a % p;
    }
    return res;
}

int main() {
    int n;
    scanf("%d", &n);
    while(n--) {
        int a, k, p;
        scanf("%d%d%d", &a, &k, &p);

        int res = qmi(a, p - 2, p);
        if(a % p) printf("%d\n", res);
        else printf("impossible\n");
    }
    return 0;
}

裴蜀定理

有一对正整数 ,那么一定存在非零整数 ,使得

的最大公约数是 能凑出来的最小的正整数

假设 ,使得 ,那么 一定是 的最大公约数的倍数。因为 的倍数, 也是 的倍数,所以 ,也一定是 的倍数。

证明

构造法

构造的方法就是扩展欧几里得算法

扩展欧几里得算法

欧几里得算法

#include <iostream>
#include <cstdio>
using namespace std;

int gcd(int a, int b) {
    if(!b) {
        return a;
    }
    return gcd(b, a % b);
}

int main() {
    int n;
    scanf("%d", &n);
    while(n--) {
        int a, b;
        scanf("%d%d", &a, &b);
    }
    return 0;
}

扩展欧几里得算法

由于 ,因此 的一组解为

#include <iostream>
#include <cstdio>
using namespace std;

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 main() {
    int n;
    scanf("%d", &n);
    while(n--) {
        int a, b;
        scanf("%d%d", &a, &b);
        exgcd(a, b, x, y);
        printf("%d %d\n", x, y);
    }
    return 0;
}

不唯一

求解线性同余方程

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

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 main() {
    int n;
    scanf("%d", &n);
    while(n--) {
        int a, b, m;
        scanf("%d%d%d", &a, &b, &m);
        int x, y;
        int d = exgcd(a, m, x, y);
        if(b % d) printf("impossible\n");
        else printf("%d\n", (ll)x * (b / d) % m);
    }
    return 0;
}
全部评论

相关推荐

03-09 20:32
技术支持工程师
牛客972656413号:成绩管理系统会不会有点太。。。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务