快速幂与筛法
快速幂:把指数拆成二进制,O(log b) 求 aᵇ mod p。筛法:从小到大划掉合数,求出所有素数。
一、快速幂:指数拆成二进制
要算 a^b mod p。把 b 写成二进制,底数不断平方(a, a², a⁴, …),哪一位是 1 就把对应的幂乘进答案。
b 的二进制:
| 步 | b 的最低位 | 当前底数 a | 乘入答案? | res |
|---|
输入 a、b、p,点"计算"看二进制拆分过程。
绿色行 = 该二进制位是 1、把当前底数乘进了答案。循环次数 = b 的位数 → O(log b),全程取模防溢出。
二、埃氏筛:划掉每个素数的倍数
从 2 开始,遇到没被划掉的数就是素数(紫),把它的倍数全划掉(红→灰)。处理完 2、3、5 后,剩下没划掉的就都是素数(绿)。
点"下一步",从素数 2 开始划。
埃氏筛 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(log b),全程取模。
② 埃氏筛
遇素数划掉其倍数,O(n log log n),好写。
遇素数划掉其倍数,O(n log log n),好写。
③ 线性筛
if (i%primes[j]==0) break; 保证最小质因子只筛一次 → O(n)。④ 用处
取模幂、逆元、素数预处理 —— 数论题的地基。
取模幂、逆元、素数预处理 —— 数论题的地基。