[学习笔记] 矩阵快速幂
在切TJOI2015时遇到需要矩阵加速的状压Dp,特此来学习一下。
入门题 poj3070 斐波那契数列
直接根据f(n) = f(n - 1) + f(n - 2) 递推 , n=0时特判一下,ans.M[1][1]就是答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; typedef long long ll; const int Mod = 1e4,N = 10; struct matrix{ int M[N][N]; int n,m; void operator * (matrix x){ int i,j,k; matrix temp; for(i=1;i<=n;i++) for(j=1;j<=x.m;j++) temp.M[i][j]=0; for(i=1;i<=n;i++) for(j=1;j<=x.m;j++) for(k=1;k<=m;k++) temp.M[i][j]=(temp.M[i][j]+M[i][k]*x.M[k][j])%Mod; for(i=1;i<=n;i++) for(j=1;j<=x.m;j++) M[i][j]=temp.M[i][j]; } }c,ans; void power(int n){ if(n<=0) return; for(; n ;n >>= 1){ if(n&1) ans * c; c * c; } } int main(){ int n; ans.n=1; ans.m=2; c.n=2; c.m=2; while(scanf("%d",&n)){ if(n==-1) break; c.M[1][1]=c.M[1][2]=c.M[2][1]=1; c.M[2][2]=0; ans.M[1][1]=1; ans.M[1][2]=0; if(n==0) printf("0\n"); else { power(n-1); printf("%d\n",ans.M[1][1]); } } return 0; }
入门题 poj3233 矩阵集合幂(应该是这么叫吧233)
这道题要先找出递推公式,也就是S_(i+1) = S_i + A^(i+1)
然后建立这样的一个转移矩阵( I O ) ( S_i ) ( S_(i+1) )
( ) x ( ) = ( )
( A A ) ( A_i ) ( A_(i+1 )
(论没有markdown的艰苦日子)
I代表单位矩阵(主元为1,其他为0的矩阵,因为把所有元素都错当成),O代表0矩阵(任何数乘以O都会得到一个0矩阵233,这样定义吗
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int N = 35; int Mod; struct matrix{ int M[N][N]; int l,r; matrix operator * (matrix x){ int i,j,k; matrix temp; temp.l=l; temp.r=x.r; for(i=1;i<=l;i++) for(j=1;j<=x.r;j++) temp.M[i][j]=0; for(i=1;i<=l;i++) for(j=1;j<=x.r;j++) for(k=1;k<=r;k++) temp.M[i][j]=(temp.M[i][j]+M[i][k]*x.M[k][j]) % Mod; return temp; } void operator + (matrix x){ int i,j; for(i=1;i<=l;i++) for(j=1;j<=r;j++) M[i][j] = (M[i][j] + x.M[i][j]) % Mod; } }ans[3],I,O,A,C[5][5],T[5][5]; void multi(){ int i,j,k; for(i=1;i<=2;i++) for(j=1;j<=2;j++) T[i][j]=O; for(i=1;i<=2;i++) for(j=1;j<=2;j++) for(k=1;k<=2;k++) T[i][j] + (C[i][k]*C[k][j]); for(i=1;i<=2;i++) for(j=1;j<=2;j++) C[i][j]=T[i][j]; } void multi2(){ int i,j,k; for(i=1;i<=2;i++) T[1][i]=O; for(j=1;j<=2;j++) for(k=1;k<=2;k++) T[1][j] + (ans[k] * C[k][j]); for(i=1;i<=2;i++) ans[i]=T[1][i]; } void power(int n){ for(; n ; n >>= 1){ if( n & 1 ) multi2(); multi(); } } int main(){ int i,j,n,k; scanf("%d%d%d",&n,&k,&Mod); for(i=1;i<=n;i++) for(j=1;j<=n;j++){ I.M[i][j]=(i==j); O.M[i][j]=0; scanf("%d",&A.M[i][j]); } I.l=I.r=O.l=O.r=A.l=A.r=n; ans[2]=A; ans[1]=A; C[1][1]=I; C[1][2]=O; C[2][1]=A; C[2][2]=A; power(k-1); for(i=1;i<=ans[1].l;i++){ for(j=1;j<=ans[1].r;j++) printf("%d ",ans[1].M[i][j]); cout<<endl; } return 0; }
入门题 HDU P2276
在转换矩阵中标出与当前行数有关的点为1,其他的点都为0,这样跑快速幂就行了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; const int N = 105; struct matrix{ int l,r; int M[N][N]; void operator * (matrix x){ int i,j,k; matrix temp; for(i=1;i<=l;i++) for(j=1;j<=r;j++) temp.M[i][j]=0; for(i=1;i<=x.l;i++) for(j=1;j<=r;j++) for(k=1;k<=l;k++) temp.M[i][j]=(temp.M[i][j]+M[k][j]*x.M[i][k])%2; for(i=1;i<=l;i++) for(j=1;j<=r;j++) M[i][j]=temp.M[i][j]; } }ans,c; void power(int n){ for(; n ; n >>= 1){ if( n & 1 ) ans * c; c * c; } } int main(){ int i,j,n; char str[N]; while(scanf("%d",&n)!=EOF){ scanf("%s",str); int len=strlen(str); ans.r=1; c.l=c.r=ans.l=len; for(i=1;i<=len;i++) ans.M[i][1]=str[i-1]-'0'; for(i=1;i<=len;i++) for(j=1;j<=len;j++) c.M[i][j]=(j==(i-1))|(j==i); c.M[1][len]=1; /*for(i=1;i<=len;i++){ for(j=1;j<=len;j++) printf("%d ",c.M[i][j]); cout<<endl; } */ power(n); for(i=1;i<=len;i++) printf("%d",ans.M[1][i]); cout<<endl; } return 0; }
普通题 HDU P5015
思路巧妙的一道题,因为a[i][j] = a[i-1][j] + a[i][j-1],所以每一列都有一个先导,因此不能直接对a[][]进行直接矩阵快速幂,而是根据行列情况递推,然后再把题目给我们的原数列乘回来,就得出我们想要的答案,其实就是递推求出a[n][m],貌似网上还有组合数学做法,玄学