Skip to content

矩阵快速幂 笔记

一、简单介绍

一个 m×n 的矩阵 A 可以和一个 n×p 的矩阵 B 相乘,得到一个大小为 n×p 的矩阵 C,其中:

Ci,j=k=1nAi,k×Bk,j

那么就可以用快速幂求矩阵幂了。

二、简单应用

1. P1962 斐波那契数列

f1=f2=1fi=fi1+fi2,求 fnmod109+7

我们假设矩阵 [fi1fi] 乘上某一个矩阵之后可以得到 [fifi+1],由于 fi=0×fi1+1×fifi+1=1×fi1+1×fi,所以:

[fi1fi]×[0111]=[fifi+1]

矩阵快速幂解决即可。

2. 数列

f1=f2=0fi=7×fi1+6×fi2+4×3i+5×i,求 fnmod109+7

同理,假设矩阵 [fi1fi4×3i5×i5] 乘上某一个矩阵可以得到 [fifi+14×3i+15×(i+1)5]

由于:

{fi=1×fifi+1=6×fi+7×fi1+3×4×3i+1×5×i+1×54×3i+1=3×4×3i5×(i+1)=1×5×i+1×55=1×5

所以:

[fi1fi4×3i5×i5]×[0600017000033000101001011]=[fifi+14×3i+15×(i+1)5]

3. CF691E Xor-sequences

给定一个数集 A,现在你需要构造一个长度为 k 的序列 B,序列 B 的元素从数集 A 中任意挑选,要求 B 中任意相邻的两个数字的异或值二进制表示中 1 的个数是 3 的倍数,求方案数,对 109+7 取模。

1n1001k,ai1018

fi,j 表示当 B 序列长度为 i,且最后一个选 j 的方案数,p(x) 表示 x 在二进制下 1 的个数,则:

fi+1,j=k=1nfi,k[3p(ajak)]

最终答案即为 i=1nfn,i

考虑优化,由于每一个 j 都只能从固定的某几个 k 转移而来(因为 ajak 的值与 i 无关),所以考虑矩阵加速。

假设 [fi,1fi,2fi,3fi,n] 乘上某个矩阵可以得到 [fi+1,1fi+1,2fi+1,3fi+1,n]c(j,k) 表示 3p(ajak)。根据上面的状态转移方程,这是一个 n×n 的矩阵,第 i 行第 j 列的值就是 c(i,j),写个函数即可。

cpp
matrix b(n, n, false);
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++) b.d[i][j] = check(a[i], a[j]);
b = pow_matrix(b, k - 1);
int ans = 0;
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++) ans = (ans + b.d[i][j]) % mod;
cout << ans;

4. P3176 [HAOI2015] 数字串拆分

本文原在 2025-04-07 18:03 发布于本人洛谷博客。

由于 f(s) 的拆分方式不关心顺序,故有 f(i)=j[im,i)f(j)f(0)=1),可理解为在 f(j) 的拆分基础上,添加一个大小为 (ij) 的数。

那么构造矩阵 Fi=[f(i)f(i1)f(i2)f(im+1)],矩阵快速幂转移即可求出 f(i)=Fi(1,1),假设转移矩阵为 A,那么有 Fi=F0×Ai

由初中数学知识,Fx+y+Fz+w=F0×(Ax×Ay+Az×Aw),故 g(s) 就相当于将字符串 s 拆成若干段,每一段的转移矩阵相乘,不同拆法的转移矩阵相加。设 Gi 表示 si 位的转移矩阵,则有转移 Gi=j[1,i]Gj1×As[j:i]

但是 |s|500As[j:i] 将会涉及指数高精。考虑递推求出所有的 As[j:i],可以将 s[j:i] 拆位,先求出每个数码的转移矩阵。设 Bi,j=Ai×10j,递推可求。接着设 Cj,i 表示 s[j:i] 的转移矩阵,则有 Cj,i=Cj+1,i×Bsj,ij

最终答案即为 (F0×G|s|)(1,1)

忽略矩阵乘法,求 B 时间 O(10×|s|),求 C 时间 O(|s|2),求 G 时间 O(|s|2)。瓶颈在求 C,G,时间复杂度为 O(m3|s|2),可以通过。

最近更新