题目:Matrix Power Series
传送门:http://poj.org/problem?id=3233
分析:
方法一:引用Matrix67大佬的矩阵十题:这道题两次二分,相当经典。首先我们知道,A^i可以二分求出。然后我们需要对整个题目的数据规模k进行二分。比如,当k=6时,有:$ S(6)= A + A^2 + A^3 + A^4 + A^5 + A^6 =\underline{(A + A^2 + A^3)} + A^3*\underline{(A + A^2 + A^3)}。 $
即对于k:如果k是偶数,就二分减小规模,$ S(k)=S(\frac{k}{2})+A^{\frac{n}{2}} *S(\frac{k}{2}) $。如果k是奇数,那么 $ S(k)=S(k-1)+A^n $; 其中 $ A^n $使用矩阵快速幂可以快速计算。
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 const int maxN=35; 5 int MOD; 6 struct Matrix{int n,a[maxN][maxN];}A,E; 7 Matrix operator+(Matrix A,Matrix B){ 8 Matrix RT{0};RT.n=A.n; 9 for(int i=0;i<RT.n;++i) 10 for(int j=0;j<RT.n;++j) 11 RT.a[i][j]=(A.a[i][j]+B.a[i][j])%MOD; 12 return RT; 13 } 14 Matrix operator*(Matrix A,Matrix B){ 15 Matrix RT{0};RT.n=A.n; 16 for(int i=0;i<A.n;++i) 17 for(int j=0;j<A.n;++j) 18 for(int k=0;k<A.n;++k) 19 (RT.a[i][j]+=A.a[i][k]*B.a[k][j])%=MOD; 20 return RT; 21 } 22 Matrix operator^(Matrix A,int n){ 23 Matrix RT=E; 24 for(;n;n>>=1){ 25 if(n&1)RT=RT*A; 26 A=A*A; 27 } 28 return RT; 29 } 30 Matrix Sum(Matrix &A,int n){ 31 if(n==1)return A; 32 if(n&1)return (A^n) + Sum(A,n-1); 33 Matrix B=Sum(A,n>>1); 34 return B+((A^(n>>1))*B); 35 } 36 int main(){ 37 for(int n,k;~scanf("%d %d %d",&n,&k,&MOD);){ 38 A.n=E.n=n; 39 for(int i=0;i<n;++i) 40 for(int j=0;j<n;++j){ 41 scanf("%d",&A.a[i][j]); 42 A.a[i][j]%=MOD;E.a[i][j]=0; 43 } 44 for(int i=0;i<n;++i)E.a[i][i]=1; 45 Matrix RT=Sum(A,k); 46 for(int i=0;i<n;++i){ 47 for(int j=0;j<n;++j) 48 printf("%d ",RT.a[i][j]); 49 puts(""); 50 } 51 } 52 return 0; 53 }
方法二:对于多项式,有秦九韶算法,而对$ A + A^2 + A^3 + … + A^k $ 这样的多项式,可以二进制优化秦九韶算法。$ S( 2^i ) $ 是可以快速计算的: $S( 2^i ) = S( 2^{(i-1)} ) * (E + A^{(2^i)} )$。记:$A[i]=A^{2^i}$、$S[i]=S(2^i)$;预处理A[i]、s[i]。
比如k=6时:$ S(6)=\underline{(A + A^2 + A^3 + A^ 4)} * A^2 + \underline{(A + A^2 )} = S(4) * A(2) + S(2) = S[2]*A[1]+S[1] $
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 const int maxN=35; 5 int MOD; 6 struct Matrix{int n,a[maxN][maxN];}A[35],B[35]; 7 Matrix operator+(Matrix A,Matrix B){ 8 Matrix RT{0};RT.n=A.n; 9 for(int i=0;i<RT.n;++i) 10 for(int j=0;j<RT.n;++j) 11 RT.a[i][j]=(A.a[i][j]+B.a[i][j])%MOD; 12 return RT; 13 } 14 Matrix operator*(Matrix A,Matrix B){ 15 Matrix RT{0};RT.n=A.n; 16 for(int i=0;i<A.n;++i) 17 for(int j=0;j<A.n;++j) 18 for(int k=0;k<A.n;++k) 19 (RT.a[i][j]+=A.a[i][k]*B.a[k][j])%=MOD; 20 return RT; 21 } 22 int main(){ 23 Matrix E{0}; 24 for(int n,k;~scanf("%d %d %d",&n,&k,&MOD);){ 25 A[0].n=E.n=n; 26 for(int i=0;i<n;++i) 27 for(int j=0;j<n;++j){ 28 scanf("%d",&A[0].a[i][j]); 29 A[0].a[i][j]%=MOD;E.a[i][j]=0; 30 } 31 for(int i=0;i<n;++i)E.a[i][i]=1; 32 for(int i=1;k>>i;++i)A[i]=A[i-1]*A[i-1]; 33 B[0]=A[0]; 34 for(int i=1;k>>i;++i)B[i]=B[i-1]*(A[i-1]+E); 35 Matrix RT{0};RT.n=n; 36 for(int i=30;~i;--i)if(k>>i&1)RT=RT*A[i]+B[i]; 37 for(int i=0;i<n;++i){ 38 for(int j=0;j<n;++j) 39 printf("%d ",RT.a[i][j]); 40 puts(""); 41 } 42 } 43 return 0; 44 }
优化一下常数(141s -> 32s):
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 const int maxN=35; 5 int MOD; 6 struct Matrix{int n,a[maxN][maxN];}A[35],B[35]; 7 Matrix operator+(Matrix A,Matrix B){ 8 Matrix RT{0};RT.n=A.n; 9 for(int i=0;i<RT.n;++i) 10 for(int j=0;j<RT.n;++j){ 11 RT.a[i][j]=A.a[i][j]+B.a[i][j]; 12 if(RT.a[i][j]>=MOD)RT.a[i][j]-=MOD; 13 } 14 return RT; 15 } 16 Matrix operator*(Matrix A,Matrix B){ 17 Matrix RT{0};RT.n=A.n; 18 for(int i=0;i<A.n;++i) 19 for(int j=0;j<A.n;++j){ 20 for(int k=0;k<A.n;++k) 21 RT.a[i][j]+=A.a[i][k]*B.a[k][j]; 22 RT.a[i][j]%=MOD; 23 } 24 return RT; 25 } 26 int main(){ 27 Matrix E{0}; 28 for(int n,k;~scanf("%d %d %d",&n,&k,&MOD);){ 29 A[0].n=E.n=n; 30 for(int i=0;i<n;++i) 31 for(int j=0;j<n;++j){ 32 scanf("%d",&A[0].a[i][j]); 33 A[0].a[i][j]%=MOD;E.a[i][j]=0; 34 } 35 for(int i=0;i<n;++i)E.a[i][i]=1; 36 for(int i=1;k>>i;++i)A[i]=A[i-1]*A[i-1]; 37 B[0]=A[0]; 38 for(int i=1;k>>i;++i)B[i]=B[i-1]*(A[i-1]+E); 39 Matrix RT{0};RT.n=n; 40 for(int i=30;~i;--i)if(k>>i&1)RT=RT*A[i]+B[i]; 41 for(int i=0;i<n;++i){ 42 for(int j=0;j<n;++j) 43 printf("%d ",RT.a[i][j]); 44 puts(""); 45 } 46 } 47 return 0; 48 }
方法三:有种很有意思的打法:构造矩阵
$ T = \left[ \begin{array}{cc} E & 0 \\ A & A \end{array} \right]$ 、 $ T^2 = \left[ \begin{array}{cc} E & 0 \\ {A + A^2} & A^2 \end{array} \right]$ 、 $ T^2 = \left[ \begin{array}{cc} E & 0 \\ {A + A^2 + A^3} & A^3 \end{array} \right]$ 、
这个矩阵的左下角就是矩阵次方和。$ T^n = \left[ \begin{array}{cc} E & 0 \\ {A + A^2 + .. A^n} & A^n \end{array} \right]$
这个矩阵可以直接快速幂求。
把RT矩阵设为 $ RT = \left[ \begin{array}{cc} 0 & E \\ 0 & 0 \end{array} \right] $,然后 $ RT \times T^n = \left[ \begin{array}{cc} {A + A^2 + .. A^n} & 0 \\ 0 & 0 \end{array} \right] $
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 const int maxN=65; 5 int MOD; 6 struct Matrix{int n,a[maxN][maxN];}A; 7 Matrix operator+(Matrix A,Matrix B){ 8 Matrix RT{0};RT.n=A.n; 9 for(int i=0;i<RT.n;++i) 10 for(int j=0;j<RT.n;++j){ 11 RT.a[i][j]=A.a[i][j]+B.a[i][j]; 12 if(RT.a[i][j]>=MOD)RT.a[i][j]-=MOD; 13 } 14 return RT; 15 } 16 Matrix operator*(Matrix A,Matrix B){ 17 Matrix RT{0};RT.n=A.n; 18 for(int i=0;i<A.n;++i) 19 for(int j=0;j<A.n;++j){ 20 for(int k=0;k<A.n;++k) 21 RT.a[i][j]+=A.a[i][k]*B.a[k][j]; 22 RT.a[i][j]%=MOD; 23 } 24 return RT; 25 } 26 Matrix Sum(Matrix A,int k){ 27 Matrix RT{0};int n=RT.n=A.n; 28 for(int t=n/2,i=0;i<n;++i)RT.a[i][i+t]=1; 29 for(;k;k>>=1){ 30 if(k&1)RT=RT*A; 31 A=A*A; 32 } 33 return RT; 34 } 35 int main(){ 36 for(int n,k;~scanf("%d %d %d",&n,&k,&MOD);){ 37 A.n=n<<1; 38 for(int i=0;i<n;++i){ 39 A.a[i][i]=1; 40 for(int j=0,t;j<n;++j){ 41 scanf("%d",&t);if(t>=MOD)t%=MOD; 42 A.a[i+n][j]=A.a[i+n][j+n]=t; 43 } 44 } 45 Matrix RT=Sum(A,k); 46 for(int i=0;i<n;++i){ 47 for(int j=0;j<n;++j) 48 printf("%d ",RT.a[i][j]); 49 puts(""); 50 } 51 } 52 return 0; 53 }