这篇 Blog 记录了一些数论代数的板子,以便不时之需。

线性筛素数

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e7 + 3;
bool isprime[MAXN];
void sieve(int n){
    memset(isprime, true, sizeof(isprime));
    isprime[0] = isprime[1] = false;
    for (int i = 2; i <= n; i++) {
        if (isprime[i])
            for (int j = i*2; j <= n; j += i) isprime[j] = false;
    }
}
int main(){
    int n, m, x;
    cin >> n >> m;
    sieve(n);
    while (m--) {
        cin >> x;
        if (isprime[x]) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
    return 0;
}

扩展欧几里得算法

求解不定方程 $ax+by=\gcd(a,b),a\geq b$

void exgcd(int a, int b, int &x, int &y) {
    if (!b) x = 1, y = 0;
    else {
        exgcd(b, a % b, y, x);
        y = y - x * (a / b);
    }
}

乘法逆元

设正整数模 $m$,$\forall a$ 满足 $(a,m)=1$,$\exists b$ 满足 $ab\equiv 1(\bmod\ m)$,称 $b$ 为模 $m$ 意义下 $a$ 的逆元。
求逆元只需解线性同余方程 $ab+mt=1$ 即可。

#include <bits/stdc++.h>
using namespace std;
int n, p;
void exgcd(int a, int b, int &x, int &y) {
    if (!b) x = 1, y = 0;
    else {
        exgcd(b, a % b, y, x);
        y -= a / b * x;
    }
}
int main() {
    scanf("%d%d", &n, &p);
    for (int i = 1; i <= n; i++) {
        int x = 0, y = 0;
        exgcd(i, p, x, y);
        x = (x + p) % p;
        printf("%d\n", x);
    }
    return 0;
}

线性筛欧拉

$\varphi(n)$表示 1 到 n 中与 n 互质的整数的个数。
$$\varphi(p^k)=p^k-p^{k-1}=(p-1)p^{k-1}$$

notprime[1] = true;
for (int i = 2; i <= n; i++) {
    if (!notprime[i]) {
        prime[++psz] = i;
        phi[i] = i - 1;
    }
    for (int j = 1; j <= psz; j++) {
        int k = i * prime[j];
        if (k > n) break;
        notprime[i * prime[j]] = true;
        if (i % prime[j] == 0) {
            phi[k] = phi[i] * prime[j];
            break;
        } else {
            phi[k] = phi[i] * (prime[j] - 1);
        }
    }
}