数论基础

求素数(单个&线性筛)

单个素数判断

枚举看n是否能整除i。\(O(\sqrt{n})\)

bool Prime(int x) {
    if (x < 2) return 0;
    for (int i = 2; i * i <= x; ++i)
        if (x % i == 0) return 0;
    return 1;
}

线性筛

保证每个和数只被最小的质因子筛掉。\(O(n)\)

int prime[N], tot;//记录素数
bool v[N];//被标记说明不是素数
void Primes(int n) {
    v[0] = v[1] = 1;//0和1不是素数
    for (int i = 2; i <= n; ++i) {
        if (!v[i]) prime[++tot] = i;//没有被标记过,是素数
        for (int j = 1; j <= tot && i * prime[j] <= n/*这里比限制n大的就不用筛了*/; ++j) {
            v[i*prime[j]] = 1;
            if (i % prime[j] == 0) break;//只被最小的质因子筛掉。
        }
    }
}

求欧拉函数(单个&线性筛)

欧拉函数

  • 1~N 中与 N 互质的数的个数称为欧拉函数,记为 \(\varphi (N)\)
  • 考虑 \(\varphi (p^k) (p\in prime)\)的取值是总数减去不互质的数的个数,不互质的数有如下 \(p^{k-1}\)

\[1\times p,2\times p,\cdots ,p^{k-1}\times p \]

  • 所以

\[\varphi (p^k)=p^k-p^{k-1}=p^k(1-\frac{1}{p}) \]

  • 再然后可以根据中国剩余定理证得欧拉函数是积性函数,所以

\[\varphi (p^{c_1}p^{c_2}\cdots p^{c_m})=\varphi (p^{c_1})\varphi (p^{c_m})\cdots \varphi (p^{c_m}) \]

  • 根据算术基本定理

\[N = p_ 1^ {c_ 1}p_ 2^{c_ 2}...p_ m^{c_m} \]

\[\varphi (N)=N\times \frac{p_1-1}{p_1}\times \frac{p_2-1}{p_2}\times ...\times\frac{p_m-1}{p_m}=N\times \prod_{质数p|N}(1-\frac{1}{p}) \]

单个求法

  • 根据欧拉函数的计算式,分解质因数即可进行求解。\(O(\sqrt{n})\)
int Phi(int n) {
    int ans = n;
    for (int i = 2; i * i <= n; ++i)
        if (n % i == 0) {
            ans = ans / i * (i - 1);//根据计算式计算
            while (n % i == 0) n /= i;
        }
    if (n > 1) ans = ans / n * (n - 1);
    //如果最后n>1,则为一个大于根号n的一个质因子
    return ans;
}

线性筛欧拉函数

  • 欧拉函数是积性函数,可以线性筛

  • 有式子

\[\varphi (i*p)= \left\{\begin{matrix} \varphi (p) \varphi (i),\gcd(p,i)=1 \\ p\times \varphi (i),\gcd(p,i)\neq 1 \end{matrix}\right.\]

  • 由于积性函数,当 \(p,i\) 互质时显然成立
  • \(p,i\) 不互质,因为 \(p\) 是质数,\(i\) 一定有 \(p\) 这个因子,所以 \(i\)\(i\times p\) 一定有相同的质因子,只是在 \(p\) 这一项的指数不一样
  • 那么我们可以将其按照欧拉函数的计算式展开,并相除,可得:

\[\frac{\varphi (i\times p)}{\varphi (i)}=\frac{i\times p \times \prod_{i=1}^m(1-\frac{1}{p_i})}{i\times \prod_{i=1}^{m}(1-\frac{1}{p_i})}=\frac{i\times p}{i}=p \]

  • 所以

\[\varphi (i\times p)=\varphi (i)\times p \]

  • 原式得证
  • 写出代码就很简单了
void Euler(int n) {
    phi[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!v[i]) prime[++tot] = i, phi[i] = i - 1;
        for (int j = 1; j <= tot && i * prime[j] <= n; ++j) {
            v[i*prime[j]] = 1;
            if (i % prime[j]) phi[i*prime[j]] = phi[i] * phi[prime[j]];
            else { phi[i*prime[j]] = phi[i] * prime[j]; break; }
        }
    }
}

扩展欧几里得算法

  • 用途:求 \(ax+by=\gcd(a, b)\) 的特解
  • 证明:

\[bx'+a\mod b\cdot y'=\gcd(b,a\mod b) \]

\[bx'+(a-\left \lfloor \frac{a}{b} \right \rfloor)y'=\gcd(b,a\mod b) \]

\[ay'+b(x'-\left \lfloor \frac{a}{b} \right \rfloor y')=\gcd(b,a\mod b) \]

所以得出

\[x=y',y=x'-\left \lfloor \frac{a}{b} \right \rfloor y' \]

Code

int Exgcd(int a, int b, int &x, int &y) {
    if (b == 0) return x = 1, y = 0, a;
    int d = Exgcd(b, a % b, x, y);
    int z = x; x = y; y = z - a / b * y;
    return d;
}
  • 上述代码 x,y 是以引用的方式传递的,此函数求出方程 \(ax+by=\gcd(a, b)\) 的一组特解并返回 a,b 的最大公约数。还有一种较为简便的写法,将上述最后一个式子的x'替换成y',y'替换成x'。得出以下结论:

\[x=x',y=y'-\left \lfloor \frac{a}{b} \right \rfloor x' \]

int Exgcd(int a, int b, int &x, int &y) {
    if (!b) return x = 1, y = 0, a;
    int d = Exgcd(b, a % b, y, x);//这里x,y是反着的
    y -= a / b * x;
    return d;
}

乘法逆元

  • 定义:若整数 \(a,m\) 互质,并且 \(b|a\),则存在一个 \(x\),使得 \(a/b\equiv a \cdot x\pmod{m}\),称 \(x\)\(b\) 的模 \(m\) 乘法逆元,记为 \(b^{-1}\pmod{m}\)
  • 求法:\(b^{-1} = b^{\varphi (p)-1}\)
  • 线性求法:
    • 推导:

\[\text{设 } m=q\cdot b+r \]

\[q\cdot b+r\equiv 0\pmod{m} \]

\[q\cdot r^{-1}+b^{-1}\equiv 0\pmod{m} \]

\[b^{-1}\equiv -q\cdot r^{-1}\pmod{m} \]

\[\text{由 }q=m/b, r=m\mod b \text{ 得} \]

\[b^{-1}\equiv -m/b\cdot (m\mod b)^{-1}\pmod{m} \]

写出来就是inv[i] = (-M / i * inv[M%i] % M + M) % M;

    inv[1] = 1;
    for (int i = 2; i <= n; ++i)
        inv[i] = 1ll * (M - M / i) * inv[M%i] % M;

排列、组合

  • 排列数:

\[A_n^m = \frac{n!}{(n-m)!} \]

  • 组合数:

\[C_n^m=\frac{n!}{m!(n-m)!} \]

  • 根据式子算就好了

Code

int Pow(int a, int k, int ans = 1) {
    for (; k; k >>= 1, a = 1LL * a * a % M)
        if (k & 1) ans = 1LL * ans * a % M;
    return ans;
}

void Pre(int n) {//fac是阶乘,inv是逆元,fi是阶乘的逆元
    fac[0] = 1;
    for (int i = 1; i <= n; ++i)
        fac[i] = 1LL * fac[i-1] * i % M;
    fai[n] = Pow(fac[n], M - 2);
    for (int i = n; i >= 1; --i)
        fai[i-1] = 1LL * fai[i] * i % M;
}

int A(int n, int m) {//排列
    return 1LL * fac[n] * fai[n-m] % M;
}
int C(int n, int m) {//组合
    return 1LL * fac[n] * fai[m] % M * fai[n-m] % M;
}
posted @ 2020-07-29 16:48  Shawk  阅读(132)  评论(3编辑  收藏  举报