CSP-J/S · 数论

快速幂与筛法

快速幂:把指数拆成二进制,O(log b) 求 aᵇ mod p。筛法:从小到大划掉合数,求出所有素数。

一、快速幂:指数拆成二进制

要算 a^b mod p。把 b 写成二进制,底数不断平方(a, a², a⁴, …),哪一位是 1 就把对应的幂乘进答案

a b p
b 的二进制:
b 的最低位当前底数 a乘入答案?res

绿色行 = 该二进制位是 1、把当前底数乘进了答案。循环次数 = b 的位数 → O(log b),全程取模防溢出。

二、埃氏筛:划掉每个素数的倍数

从 2 开始,遇到没被划掉的数就是素数(紫),把它的倍数全划掉(红→灰)。处理完 2、3、5 后,剩下没划掉的就都是素数(绿)。

埃氏筛 O(n log log n)。线性筛让每个合数只被"最小质因子"划一次,做到 O(n)(见下方代码)。

三、模板(C++14)

long long qpow(long long a, long long b, long long p) {
    long long res = 1 % p;              // 防 p = 1
    a %= p;
    while (b) {
        if (b & 1) res = res * a % p;   // 当前最低位是 1,乘进答案
        a = a * a % p;                  // 底数平方
        b >>= 1;                         // 看下一位
    }
    return res;
}

// 线性筛:每个合数只被它的最小质因子筛一次 → O(n)
void sieve(int n) {
    for (int i = 2; i <= n; i++) {
        if (!notPrime[i]) primes[cnt++] = i;
        for (int j = 0; j < cnt && i * primes[j] <= n; j++) {
            notPrime[i * primes[j]] = true;
            if (i % primes[j] == 0) break;   // 灵魂:保证只被最小质因子筛
        }
    }
}

取模下求 1/m! 要用快速幂求逆元(费马小定理)。组合计数见 组合数学

小结

① 快速幂
指数二进制拆分、底数反复平方,O(log b),全程取模。
② 埃氏筛
遇素数划掉其倍数,O(n log log n),好写。
③ 线性筛
if (i%primes[j]==0) break; 保证最小质因子只筛一次 → O(n)。
④ 用处
取模幂、逆元、素数预处理 —— 数论题的地基。