快速幂
快速幂的作用:
就是为了快速的算出\(a^k(mod)p\),先看朴素算法,如果算a^k那么得用一个for循环,效率为\(O(n)\),但是如果使用快速幂那么效率就成变成了\(O(logn)\),所以说效率还是很高的。
原理:
因为k有\([log_2k] + 1\)个二进制位,所以我们需要预处理出\(a^{2^0}(mod)p\), \(a^{2^1}(mod)p\),.....\(a^{2^{log_2k}}(mod)p\),一共有\(log_2k\)个,因此时间复杂度是\(O(logk)\),然后再组合出\(a^k\)即可,就是将\(a^k\)拆成\(a^{i_1}*a^{i_2}...\)的形式,整理一下就是\(a^{i_1 + i_2 + ....}\)的形式,因此我们可以分两步:
- 第一步先处理k的二进制:假设\((k)_10 = (110110)_2\),那么k的二进制形式就是\(2^5 + 2^4 + 2^2 + 2^1\),因此二进制位上为1的加上即可
- 第二步:快速处理出\(a^{2^0}\), \(a^{2^1}\)....,很容易发现,\(a^{2^1} = {(a^{2^0}})^2\),即每一项是前一项的平方
都处理完之后,就可以把\(a^k\)拆成\(a^{i_1}*a^{i_2}...\)的形式了
感觉自己说的都一懵一懵的,举个例子:
假设要算\(4^5(mod)9\),那么我们可以将\(5\)拆解成它的二进制形式\((0101)\),于是:
\[4^5(mod)9 = 4^{2^0}(mod)9 * 4^{2^2}(mod)9 = 4^{2^0 + 2^2}(mod)9
\]
代码:
int qmi(int a, int k, int p) {
int res = 1;
if(k == 0 && p == 1) return 0; //可能会出现k == 0 p == 1的情况, 需要特判
while(k) {
if(k & 1) res = (LL)res * a % p; //不要忘了long long很重要
k >>= 1;
a = (LL)a * a % p;
}
return res;
}
矩阵快速幂
与普通的快速幂类似,求\(A^k\)(A是一个矩阵)只不过是多了一个矩阵乘法,再有就是注意一些细节了,long long的使用,直接看代码吧
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 110, Mod = 1e9 + 7;
long long n, k;
void mul(LL a[][N], LL b[][N], LL c[][N]) {
LL temp[N][N] = {0};
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
temp[i][j] = (temp[i][j] + (LL)b[i][k] * c[k][j] % Mod) % Mod; //防止溢出
}
}
}
memcpy(a, temp, sizeof temp);
}
int main() {
LL a[N][N], res[N][N];
scanf("%lld%lld", &n, &k); //因为都是long long类型 因此用lld读入
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
scanf("%lld", &a[i][j]);
res[i][j] = a[i][j];
}
}
k--;
while(k) {
if(k & 1) mul(res, res, a);
mul(a, a, a);
k >>= 1;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
printf("%lld ", res[i][j]);
}
puts("");
}
return 0;
}
还有一些典型的例题 比如fib求值,求前n项和等等,会填坑的