矩阵相关
矩阵前置芝士
矩阵:看成一个二维数组就好了.
矩阵加法:
本人认为没用
条件
:两个大小相同的矩阵
对应位置相加,形成相同大小的矩阵.
矩阵减法:
本人认为没用
条件
:两个大小相同的矩阵
对应位置相减,形成相同大小的矩阵.
矩阵乘法:
这个很重要
条件
:两个大小不完全一样的矩阵,必须保证矩阵\(A(n*k)\),矩阵B大小是\(k*m\)的,形成的矩阵C大小是\(n*m\)的
C矩阵中的x,y位置是A矩阵的X行,B矩阵的Y列相加.
代码:
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= m;++ j) {
for(int l = 1;l <= k;++ l)
C[i][j] += A[i][k] + B[k][j];
}
}
性质:
矩阵乘法具有结合律:
\(A*B*C = (A*B)*C = A*(B*C)\)
矩阵快速幂
ps:使用矩阵快速幂的时候一定要有模数.
根据矩阵乘法具有结合律的性质,我们可以做矩阵的快速幂:
例题---Luogu矩阵快速幂
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
const int mod = 1e9 + 7;
const int X = 110;
ll n,k;
struct Node {
ll a[X][X];
void clear() {
memset(a,0,sizeof(a));
}
}res,ans;
Node mul(Node a,Node b) {
Node tmp;
tmp.clear();
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= n;++ j) {
for(int k = 1;k <= n;++ k) {
tmp.a[i][j] = (tmp.a[i][j] + (a.a[i][k] * b.a[k][j]) % mod ) % mod;
}
}
}
return tmp;
}
void fast_pow(ll k) {
for(int i = 1;i <= n;++ i)
ans.a[i][i] = 1;
for(;k;k >>= 1,res = mul(res,res)) {
if(k & 1)ans = mul(ans,res);
}
return ;
}
int main() {
scanf("%lld%lld",&n,&k);
for(int i = 1;i <= n; ++i ) {
for(int j = 1;j <= n;++ j) {
scanf("%lld",&res.a[i][j]);
}
}
ans.clear();
fast_pow(k);
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= n;++ j) {
printf("%lld ",ans.a[i][j]);
}
printf("\n");
}
return 0;
}
/*
5
5
1 1 1 1 1
1 0 0 1 0
0 0 2 3 1
0 0 0 1 0
5 5 5 5 5
*/
斐波那契数列
我们可以通过矩阵快速幂来优化斐波那契数列斐波那契数列:$$f[n] = f[n - 1] + f[n - 2]$$
放道例题:
斐波那契数列---Luogu1962
如何做:
斐波那契数列的通项公式???
显然不行,有精度问题.
那我们来做矩阵快速幂
题目中有f[1] = 1,f[2] = 1
先构造矩阵(先不用管为什么这样构造):
表示
尝试往后推一项
我们想要矩阵成这个样子
那我们要乘一个矩阵
首先根据矩阵的定义,我们必须要构造一个\(2*2\)的矩阵
首先给出构造好的矩阵:
模拟一下就好了.
构造就是 猜 根据生成的矩阵来构造中间的这个矩阵
我们再回过头来看一下,为什么刚开始的矩阵是
因为f[3]要用到这两项啊.
记住,矩阵快速幂中间的矩阵不可改变.
这样我们就可以进行中间的矩阵\(n -2\)次幂的运算来获得一个矩阵啦,
然后用刚开始的矩阵与这个矩阵相乘就得到第n项了.
注意特判\(n == 1,n == 2\)的特殊情况
#include <iostream>
#include <cstring>
#include <cstdio>
#define ll long long
const int maxN = 100 + 7;
const ll mod = 1000000007;
using namespace std;
ll n,m,k;
struct Marix{
ll s[maxN][maxN];
void clear() {
memset(s,0,sizeof(s));
return;
}
}ans,now,a;
Marix work(Marix a,Marix b) {
Marix c;
c.clear();
for(ll i = 1;i <= n;++ i)
for(ll j = 1;j <= n;++ j)
for(ll k = 1;k <= n;++ k)
c.s[i][j] = ( c.s[i][j] % mod+ ( a.s[i][k] * b.s[k][j] ) % mod ) % mod;
return c;
}
void fast_pow() {
ans.clear();
for(ll i = 1;i <= n;++ i) {
ans.s[i][i] = 1;
}
for(now = a;k;k >>= 1,now = work(now,now)) {
if(k & 1) ans = work(ans,now);
}
Marix Q;
Q.s[1][1] = 1;Q.s[1][2] = 1;
ans = work(Q,ans);
printf("%lld",ans.s[1][1]);
}
int main() {
n = 2;
scanf("%lld",&k);
if(k == 1) {
printf("1");
return 0;
}
if(k == 2) {
printf("1");
return 0;
}
k -= 2;
a.s[1][1] = 1;a.s[1][2] = 1;
a.s[2][1] = 1;a.s[2][2] = 0;
fast_pow();
return 0;
}
矩阵递推相关
给道例题:Luogu1939
建议自己先构造一下:
开始矩阵:
中间的矩阵:
#include <iostream>
#include <cstring>
#include <cstdio>
#define ll long long
const int maxN = 100 + 7;
const int mod = 1e9 + 7;
using namespace std;
ll n,m,k;
struct Marix{
ll s[maxN][maxN];
void clear() {
memset(s,0,sizeof(s));
return;
}
}ans,now,a;
Marix work(Marix a,Marix b) {
Marix c;
c.clear();
for(ll i = 1;i <= n;++ i)
for(ll j = 1;j <= n;++ j)
for(ll k = 1;k <= n;++ k)
c.s[i][j] = ( c.s[i][j] % mod+ ( a.s[i][k] * b.s[k][j] ) % mod ) % mod;
return c;
}
void fast_pow() {
ans.clear();
for(ll i = 1;i <= n;++ i) {
ans.s[i][i] = 1;
}
for(now = a;k;k >>= 1,now = work(now,now)) {
if(k & 1) ans = work(ans,now);
}
Marix Q;
Q.s[1][1] = 1;Q.s[1][2] = 1;Q.s[1][3] = 1;
ans = work(Q,ans);
printf("%lld\n",ans.s[1][3]);
}
int main() {
n = 3;
int T;
scanf("%d",&T);
while(T --) {
a.s[1][3] = a.s[2][1] = a.s[3][2] = a.s[3][3] = 1;
scanf("%lld",&k);
k -= 3;
if(k <= 0) printf("1\n");
else fast_pow();
}
return 0;
}
矩阵快速幂优化DP
设置状态f[i][j][k]表示i到j走k步的方案数
设f[i][j][1] = 0/1表示i到j有没有路(0代表无路,1代表有)
枚举中间点Q
\(f[i][j][k] = \sum_{Q=1}^{n}f[i][Q][k - 1] * f[Q][j][1]\)
HDU2157
#include <iostream>
#include <cstdio>
#include <cstring>
const int maxN = 20 + 7;
const int mod = 1000;
using namespace std;
struct Matrix
{
int s[maxN][maxN];
Matrix() {memset(s,0,sizeof(s));}
};
int n,m;
Matrix operator * (const Matrix &a,const Matrix &b) {
Matrix c;
for(int i = 1;i <= n;++ i) {
for(int j = 1;j <= n;++ j) {
for(int k = 1;k <= n;++ k) {
c.s[i][j] = (c.s[i][j] + a.s[i][k] * b.s[k][j] % mod) % mod;
}
}
}
return c;
}
Matrix fast_pow(Matrix a,int nn) {
Matrix ans;
for (int i = 1;i <= n; ++i)
ans.s[i][i] = 1;
for (; nn; nn >>= 1,a = a * a)
{
if(nn & 1) ans = ans * a;
}
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&m) && (n || m)) {
Matrix q;
for(int i = 1,u,v;i <= m;++ i) {
scanf("%d%d",&u,&v);
q.s[u + 1][v + 1] = 1;
}
int T;
scanf("%d",&T);
int s,t,k;
while(T --) {
scanf("%d%d%d",&s,&t,&k);
Matrix b;
b = fast_pow(q,k);
printf("%d\n", b.s[s + 1][t + 1]);
}
}
return 0;
}