快速幂

快速幂是在 O(logk)O(\log k) 的复杂度下计算 akmodpa^k\bmod p 的结果。

可以用二进制的方式理解,令 k=bi2ik=\sum b_i2^i,那么 ak=abi2i=(a2i)bia^k=a^{\sum b_i2^i}=\prod (a^{2^i})^{b_i},所以在每一次循环中处理出 aa2a\gets a^2 即可,相当于处理出 ai=ai12=(a2i1)2=a2ia_i=a_{i-1}^2=(a^{2^{i-1}})^2=a^{2^i},类似于一个滚动数组优化。

1
2
3
4
5
6
7
8
9
int qmi(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1) res = 1ll * res * a % p;
a = 1ll * a * a % p;
k >>= 1;
}
return res;
}

光速幂

当底数不变时,预处理出 a1aB1a^1\sim a^{B-1}aBaBn/Ba^B\sim a^{B\lfloor n/B\rfloor},然后询问 aka^k 将其化为 akmodB+k/BB=akmodB×(aB)k/Ba^{k\bmod B+\lfloor k/B\rfloor B}=a^{k\bmod B}\times (a^B)^{\lfloor k/B\rfloor} 即可,所以这样预处理的复杂度是 O(B+n/B)O(B+n/B),当 B=nB=\sqrt n 时最优为 O(n)O(\sqrt n) 的复杂度。

为了方便书写,这里让 aia^iaBia^{Bi} 最高都处理到 B1B-1,这样表达的最大幂次是 B(B1)+B1=B21B(B-1)+B-1=B^2-1,也就若幂次最高为 nn 只要让 B21nB^2-1\ge n 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int el[B], blo[B];

void init(int x) {
el[0] = blo[0] = 1;

for (int i = 1; i < B; i++)
el[i] = el[i - 1] * x % P;

int b = el[B - 1] * x % P;

for (int i = 1; i < B; i++)
blo[i] = blo[i - 1] * b % P;
}

int get(int k) {
return blo[k / B] * el[k % B] % P;
}