快速幂

现在有一道题摆在你面前,让你求 \(a^b\mod p\) 的值。那么如果你还不知道世界上有快速幂这样美妙的东西,你一定会以光速敲一份 \(O(b)\) 暴力代码。那如果把 b 提到 \(10^18\) 呢?显然暴力是过不去了。我们需要一种更加优雅的方法。
于是我们引入快速幂
如果 b 是偶数,很显然 \(a^b\) 可以拆成两个 \(a^{b/2}\) 的乘积;同理,若 b 是奇数,\(a^b\) 也可以拆成 \(a^{\lfloor b/2 \rfloor}\) x \(a^{\lfloor b/2 \rfloor}\) x \(a\)。有了这个柿子,代码的时间复杂度瞬间降至 \(O( \log p )\)
一份新鲜的代码就出炉了。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int a,b,p;
	scanf("%d%d%d",&a,&b,&p);
	int bb=b;
	long long ans=1;
	for(long long i=a;bb!=0;bb/=2,i=i*i%p){
		if(bb%2==1) ans=ans*i%p;
	}
	//对子树进行二进制拆分。
	printf("%d^%d mod %d=%d",a,b,p,ans);
	return 0;
}

矩阵快速幂

那么我们现在现在引入矩阵乘法。矩阵乘法简单理解就是一个 \(n\) x \(m\) 的矩阵 A 和 \(m\) x \(q\) 的矩阵 B 的乘积。到底怎么算呢?具体地说,答案矩阵 ans 的第 i 行第 j 列,也就是 ans[i][j],等于 \(\sum_{k=1}^m A[i][k]\) x \(B[k][j]\)。说白了就是对应行乘以对应列。
于是又有一个进阶问题横空出世:矩阵的幂怎么求?
了解了矩阵乘法之后,矩阵快速幂的实现方法其实就呼之欲出了。
创建结构体,重载乘法运算符,直接把矩阵和新运算套进快速幂板子里就好啦!
又是一份新鲜出炉的代码。

#include<bits/stdc++.h>
using namespace std;
int n;
long long mod=1e9+7;
struct Matrix{
	long long a[105][105];
	Matrix operator * (const Matrix &ls) const {
		Matrix ans;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++){
				ans.a[i][j]=0;
				for(int k=1;k<=n;k++) ans.a[i][j]=(ans.a[i][j]+a[i][k]*ls.a[k][j]%mod)%mod;
			}
		return ans; 
	}
};
void ksm(Matrix d,long long k){
	Matrix ans;
	for(int i=1;i<=n;i++) ans.a[i][i]=1;
	for(Matrix i=d;k!=0;k/=2,i=i*i)
		if(k%2!=0) ans=ans*i;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) printf("%lld ",ans.a[i][j]%mod);
		printf("\n");
	}
	return;
}
int main(){
	long long k;
	scanf("%d%lld",&n,&k);
	Matrix in;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) scanf("%lld",&in.a[i][j]);
	ksm(in,k);
	return 0;
}

练手题:
洛谷P3390 【模板】矩阵快速幂 \(\And\) 洛谷P1349 广义斐波那契数列 \(\And\) 洛谷P1939 矩阵加速(数列)