<!--markdown--># 倍增法(快速幂 & ST 表)

引入

求 2^1000000 的个位数?如果老老实实乘 100 万次肯定超时。快速幂能在 O(logn) 内算 a^n。而求一段区间的最值,每次都扫一遍是 O(n),用 ST 表(Sparse Table) 能预处理后在 O(1) 回答。它们背后的共同思想,就是倍增:把一次大步拆成多个「翻倍的小步」,用预处理好的「2 的幂次」结果拼出答案。

---

一、快速幂(Fast Pow)

1. 思路

把指数 n 写成二进制,例如 n = 13 = 1101₂ = 8+4+1,那么:

a^13 = a^8 × a^4 × a^1

我们只需不断把 a 自己翻倍(a → a² → a⁴ → a⁸),再根据 n 的二进制位决定乘不乘当前这个翻倍结果。这样最多乘 log n 次。

2. 代码

cpp

long long fastPow(long long a, long long n, long long mod) {

long long res = 1;

a %= mod;

while (n > 0) {

if (n & 1) res = res * a % mod; // 当前二进制位是 1,乘上

a = a * a % mod; // a 自己翻倍

n >>= 1; // n 右移一位

}

return res;

}

  • 复杂度 O(logn)。
  • 常配合取模,防止溢出。

3. 应用

  • 求 a^n mod p。
  • 矩阵快速幂:求斐波那契第 n 项 F(n)(用转移矩阵的 n 次幂)。
  • 模运算下的逆元(配合费马小定理)。

---

二、ST 表(稀疏表)——区间最值 RMQ

1. 思想

预处理出所有长度为 2 的幂的区间的最值。st[i][j] 表示从 i 开始、长度 2^j 的区间最值。任意区间 [L,R] 的最值,可以由两个长度为其「最大不超过区间长的 2 的幂」的区间重叠拼出。

2. 预处理与查询

cpp

// st[i][j]:以 i 为起点、长度 2^j 的区间最值(这里以最大值为例)

int log2s[N];

int st[N][LOG];

void build(int n, int a[]) {

for (int i = 1; i <= n; i++) st[i][0] = a[i];

for (int j = 1; (1 << j) <= n; j++)

for (int i = 1; i + (1 << j) - 1 <= n; i++)

st[i][j] = max(st[i][j-1], st[i + (1 << (j-1))][j-1]);

}

int query(int L, int R) {

int k = log2s[R - L + 1]; // 最大的 2^k <= 区间长

return max(st[L][k], st[R - (1 << k) + 1][k]); // 两块重叠覆盖整个区间

}

  • 预处理 O(nlogn),单次查询 O(1)。
  • 只能处理静态数组(不可修改),适合只查不改的 RMQ。

---

三、倍增的其他应用(竞赛常见)

  • 树上倍增求 LCA:预处理每个点向上跳 2^j 步到的祖先,查询时按二进制位跳。
  • 二分答案的验证中的倍增跳跃、稀疏图上的可达查询。
  • 核心思想:「跳一步」分解成「跳多个 2 的幂」,用预处理省时间。

---

四、考点提示

  • 快速幂完善题:if(n&1) res=res*a%mod;、a=a*a%mod、n>>=1 三行填空高频。
  • ST 表:k = log2(len)、max(st[L][k], st[R-2^k+1][k]) 的写法、st[i][j] = max(st[i][j-1], st[i+2^(j-1)][j-1])。
  • 判断题:ST 表不可修改数据;快速幂时间复杂度是 O(logn) 而非 O(n)。

一句话记忆:倍增 = 用「翻倍的步长」预处理 + 按二进制拼答案;快速幂算次幂、ST 表答区间最值、树上倍增找 LCA,全是它的应用。