POJ3233]Matrix Power Series
題目: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)View Code11 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 returnRT; 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 }View Code
優化一下常數(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 }