矩阵是种神奇的东西,无奈我这种数学渣对于他的理解只能是表面。
快速幂应该都很熟悉,实际上把快速幂里面的乘法换成矩阵乘法就可以变为矩阵快速幂
上份模板:
1 typedef vector<int>vec; 2 typedef vector<vec>mat; 3 typedef long long ll; 4 mat mul(mat &A,mat &B){ 5 mat C (A.size(),vec(B[0].size())); 6 for(int i=0;i<A.size();++i){ 7 for(int k=0;k<B.size();++k){ 8 for(int j=0;j<B[0].size();++j){ 9 C[i][j]=(C[i][j]+(A[i][k]*B[k][j])%mod)%mod; 10 } 11 } 12 } 13 return C; 14 } 15 mat pow(mat A,ll n){ 16 mat B (A.size(),vec(A.size())); 17 for(int i=0;i<A.size();++i)B[i][i]=1; 18 while(n>0){ 19 if(n&1)B = mul(B,A); 20 A = mul(A,A); 21 n>>=1; 22 } 23 return B; 24 }
矩阵快速幂有什么作用呢?优化递推关系!!!!
先看一下求第n项费布那切数列f[n]=f[n-1]+f[n-2]
这样我们就可以快速的求得第n项
题意:用红蓝绿黄去涂n个块,问红绿都为偶数块的方案数
假设当前前i个已经涂完其中红绿都为偶数有ai种,一奇一偶为bi,都为奇为ci ,那么对于i+1 有
a(i+1)=2*a(i)+b(i)
b(i+1)=2*a(i)+2*b(i)+2*c(i)
c(i+1)=b(i+1)+2*c(i)
这样就转化为求a(n)了
利用矩阵快速幂可以很好的解决。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <set> 5 #include <algorithm> 6 #include <map> 7 #include <queue> 8 #include<cmath> 9 #include<vector> 10 #define maxn 50010 11 #define maxm 100010 12 #define mod 10007 13 #define INF 0x3f3f3f3f 14 using namespace std; 15 typedef vector<int>vec; 16 typedef vector<vec>mat; 17 typedef long long ll; 18 mat mul(mat &A,mat &B){ 19 mat C (A.size(),vec(B[0].size())); 20 for(int i=0;i<A.size();++i){ 21 for(int k=0;k<B.size();++k){ 22 for(int j=0;j<B[0].size();++j){ 23 C[i][j]=(C[i][j]+(A[i][k]*B[k][j])%mod)%mod; 24 } 25 } 26 } 27 return C; 28 } 29 mat pow(mat A,ll n){ 30 mat B (A.size(),vec(A.size())); 31 for(int i=0;i<A.size();++i)B[i][i]=1; 32 while(n>0){ 33 if(n&1)B = mul(B,A); 34 A = mul(A,A); 35 n>>=1; 36 } 37 return B; 38 } 39 int n; 40 void solve(){ 41 mat A(3,vec(3)); 42 A[0][0]=2;A[0][1]=1;A[0][2]=0; 43 A[1][0]=2;A[1][1]=2;A[1][2]=2; 44 A[2][0]=0;A[2][1]=1;A[2][2]=2; 45 A = pow(A,n); 46 printf("%d\n",A[0][0]); 47 } 48 int main (){ 49 int t; 50 scanf("%d",&t); 51 while(t--){ 52 scanf("%d",&n); 53 solve(); 54 } 55 }
题意:给你一个4*n的方格,问用1*2的去填有多少种方案。
这里给出Matrix67大神的一篇博客点我
里面的问题九就是这样一个问题,主要还是转移矩阵的构造,对于行数比较小的情况我们可以自己手动推,也更有助于理解。
但是实际上可以通过计算机来计算这个转移的矩阵,有点类似DFA。
我们只需要给他转移的规则就可以构造出这个矩阵(记忆化搜索)具体的看代码吧
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <set> 5 #include <algorithm> 6 #include <map> 7 #include <queue> 8 #include<cmath> 9 #include<vector> 10 #define maxn 50010 11 #define maxm 100010 12 #define INF 0x3f3f3f3f 13 using namespace std; 14 typedef vector<int>vec; 15 typedef vector<vec>mat; 16 typedef long long ll; 17 int mod; 18 mat mul(mat &A,mat &B){ 19 mat C (A.size(),vec(B[0].size())); 20 for(int i=0;i<A.size();++i){ 21 for(int k=0;k<B.size();++k){ 22 for(int j=0;j<B[0].size();++j){ 23 C[i][j]=(C[i][j]+(A[i][k]*B[k][j])%mod)%mod; 24 } 25 } 26 } 27 return C; 28 } 29 mat pow(mat A,ll n){ 30 mat B (A.size(),vec(A.size())); 31 for(int i=0;i<A.size();++i)B[i][i]=1; 32 while(n>0){ 33 if(n&1)B = mul(B,A); 34 A = mul(A,A); 35 n>>=1; 36 } 37 return B; 38 } 39 int n; 40 int dp[17][17]; 41 void dfs(int i,int s){ 42 if(dp[i][s]==1)return; 43 dp[i][s]=1; 44 for(int k=0;k<4;++k){ 45 if(k<3&&!(s&(1<<k))&&!(s&(1<<(k+1)))){ 46 dfs(i,s+(1<<k)+(1<<(k+1))); 47 } 48 } 49 } 50 void solve(){ 51 memset(dp,0,sizeof(dp)); 52 for(int i=0;i<(1<<4);++i)dfs(i,(1<<4)-1-i); 53 mat A(16,vec(16)); 54 mat I(16,vec(16)); 55 for(int i=0;i<16;++i){ 56 for(int j=0;j<16;++j){ 57 if(i==j)I[i][j]=1; 58 else I[i][j]=0; 59 A[i][j]=dp[i][j]; 60 // cout<<A[i][j]<<" "; 61 } 62 //cout<<endl; 63 } 64 A = pow(A,n+1); 65 A = mul(I,A); 66 printf("%d\n",A[15][0]); 67 } 68 int main (){ 69 int t; 70 while(scanf("%d%d",&n,&mod)!=EOF){ 71 if(mod==0&&n==0)break; 72 solve(); 73 } 74 }
题意给你矩阵A 求sum(A^0+A^1+...+A^k)%m
有了这个就可以直接搞了。实际上有更快的算法,基本原理就是Matrix里面写的
A^1+A^2+...+A^6 = (A^1+A^2+A^3)+A^3* (A^1+A^2+A^3)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <set> 5 #include <algorithm> 6 #include <map> 7 #include <queue> 8 #include<cmath> 9 #include<vector> 10 #define maxn 50010 11 #define maxm 100010 12 #define INF 0x3f3f3f3f 13 using namespace std; 14 typedef vector<int>vec; 15 typedef vector<vec>mat; 16 typedef long long ll; 17 int mod; 18 mat mul(mat &A,mat &B){ 19 mat C (A.size(),vec(B[0].size())); 20 for(int i=0;i<A.size();++i){ 21 for(int k=0;k<B.size();++k){ 22 for(int j=0;j<B[0].size();++j){ 23 C[i][j]=(C[i][j]+(A[i][k]*B[k][j])%mod)%mod; 24 } 25 } 26 } 27 return C; 28 } 29 mat pow(mat A,ll n){ 30 mat B (A.size(),vec(A.size())); 31 for(int i=0;i<A.size();++i)B[i][i]=1; 32 while(n>0){ 33 if(n&1)B = mul(B,A); 34 A = mul(A,A); 35 n>>=1; 36 } 37 return B; 38 } 39 int n; 40 mat M; 41 void solve(int k){ 42 mat A(2*n,vec(2*n)); 43 for(int i=0;i<n;++i){ 44 for(int j=0;j<n;++j){ 45 A[i][j]=M[i][j]; 46 } 47 A[i+n][i]=A[i+n][i+n]=1; 48 } 49 A = pow(A,k+1); 50 for(int i=0;i<n;++i){ 51 for(int j=0;j<n;++j){ 52 int a = A[n+i][j]%mod; 53 if(i==j)a = (a+mod-1)%mod; 54 printf("%d%c",a,j+1==n?'\n':' '); 55 } 56 57 } 58 } 59 int main (){ 60 int k; 61 while(scanf("%d%d%d",&n,&k,&mod)!=EOF){ 62 M = mat(n,vec(n)); 63 for(int i=0;i<n;++i){ 64 for(int j=0;j<n;++j){ 65 scanf("%d",&M[i][j]); 66 } 67 } 68 solve(k); 69 } 70 }
矩阵快速幂的题目主要还是构造矩阵,如何把题目转化为矩阵这一步才是关键!!!!
来源:https://www.cnblogs.com/shuzy/p/3826612.html