题目: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[ egin{array}{cc} E & 0 \ A & A end{array} ight]$ 、 $ T^2 = left[ egin{array}{cc} E & 0 \ {A + A^2} & A^2 end{array} ight]$ 、 $ T^2 = left[ egin{array}{cc} E & 0 \ {A + A^2 + A^3} & A^3 end{array} ight]$ 、
这个矩阵的左下角就是矩阵次方和。$ T^n = left[ egin{array}{cc} E & 0 \ {A + A^2 + .. A^n} & A^n end{array} ight]$
这个矩阵可以直接快速幂求。
把RT矩阵设为 $ RT = left[ egin{array}{cc} 0 & E \ 0 & 0 end{array} ight] $,然后 $ RT imes T^n = left[ egin{array}{cc} {A + A^2 + .. A^n} & 0 \ 0 & 0 end{array} ight] $
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 }
题目: Gauss Fibonacci
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1588
分析:Fibonacci数列可用矩阵:$ Fib = left[ egin{array}{cc} 0 & 1 \ 1 & 1 end{array} ight] $ 表示,f(i)=$Fib^n$的左下角元素的值。
然后即求 $ Fib^b + Fib^{k+b} + Fib^{2*k+b} + ... Fib^{(n-1)*k+b} $
即:$ Fib^b * (E + Fib^k + Fib^{2*k} + Fib^{3*k} + ... +Fib^{(n-1)*k} $ = $ Fib^b * (E + (Fib^k) + {(Fib^k)}^2 + {(Fib^k)}^3 + ... +{(Fib^k)}^{(n-1)} )$
令 $ A=Fib^k $;
则求: $ Fib^b * (E + A + A^2 + A^3 +.. A^n) $
就和上面一样了。
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 typedef long long LL; 5 const int maxN=2; 6 LL MOD; 7 struct Matrix{LL a[maxN][maxN];}A[35],B[35],E,Fib; 8 Matrix operator+(Matrix A,Matrix B){ 9 Matrix RT{0}; 10 for(int i=0;i<2;++i) 11 for(int j=0;j<2;++j){ 12 RT.a[i][j]=A.a[i][j]+B.a[i][j]; 13 if(RT.a[i][j]>=MOD)RT.a[i][j]-=MOD; 14 } 15 return RT; 16 } 17 Matrix operator*(Matrix A,Matrix B){ 18 Matrix RT{0}; 19 for(int i=0;i<2;++i) 20 for(int j=0;j<2;++j){ 21 for(int k=0;k<2;++k) 22 RT.a[i][j]+=A.a[i][k]*B.a[k][j]; 23 RT.a[i][j]%=MOD; 24 } 25 return RT; 26 } 27 Matrix operator^(Matrix A,LL n){ 28 Matrix RT=E; 29 for(;n;n>>=1){ 30 if(n&1)RT=RT*A; 31 A=A*A; 32 } 33 return RT; 34 } 35 int main(){ 36 for(LL k,b,n;~scanf("%lld %lld %lld %lld",&k,&b,&n,&MOD);){ 37 --n; 38 Fib.a[0][0]=0;Fib.a[0][1]=1; 39 Fib.a[1][0]=1;Fib.a[1][1]=1; 40 E.a[0][0]=E.a[1][1]=1; 41 E.a[1][0]=E.a[0][1]=0; 42 43 A[0]=Fib^k; 44 for(int i=1;n>>i;++i)A[i]=A[i-1]*A[i-1]; 45 B[0]=A[0]; 46 for(int i=1;n>>i;++i)B[i]=B[i-1]*(A[i-1]+E); 47 48 Matrix RT{0}; 49 for(int i=30;~i;--i)if(n>>i&1)RT=RT*A[i]+B[i]; 50 RT=(RT+E)*(Fib^b); 51 printf("%lld ",RT.a[1][0]); 52 } 53 return 0; 54 }
#include <iostream> #include <cstdio> using namespace std; typedef long long LL; const int maxN=4; LL MOD; struct Matrix{int n;LL a[maxN][maxN];}A,Fib; Matrix operator+(Matrix A,Matrix B){ Matrix RT{0}; for(int i=0;i<2;++i) for(int j=0;j<2;++j){ RT.a[i][j]=A.a[i][j]+B.a[i][j]; if(RT.a[i][j]>=MOD)RT.a[i][j]-=MOD; } return RT; } Matrix operator*(Matrix A,Matrix B){ Matrix RT{0};int n=RT.n=A.n; for(int i=0;i<n;++i) for(int j=0;j<n;++j){ for(int k=0;k<n;++k) RT.a[i][j]+=A.a[i][k]*B.a[k][j]; RT.a[i][j]%=MOD; } return RT; } Matrix operator^(Matrix A,LL n){ Matrix RT; RT.n=A.n; for(int i=0;i<RT.n;++i)for(int j=0;j<RT.n;++j)RT.a[i][j]=0; for(int i=0;i<RT.n;++i)RT.a[i][i]=1; for(;n;n>>=1){ if(n&1)RT=RT*A; A=A*A; } return RT; } int main(){ for(LL k,b,n;~scanf("%lld %lld %lld %lld",&k,&b,&n,&MOD);){ --n; Fib.n=2; Fib.a[0][0]=0;Fib.a[0][1]=1; Fib.a[1][0]=1;Fib.a[1][1]=1; A=Fib^k; A.n=4; A.a[2][0]=A.a[2][2]=A.a[0][0]; A.a[2][1]=A.a[2][3]=A.a[0][1]; A.a[3][0]=A.a[3][2]=A.a[1][0]; A.a[3][1]=A.a[3][3]=A.a[1][1]; A.a[0][0]=1;A.a[0][1]=0; A.a[1][0]=0;A.a[1][1]=1; A=A^n; A.n=2; A.a[0][0]=A.a[2][0]+1;A.a[0][1]=A.a[2][1]+0; A.a[1][0]=A.a[3][0]+0;A.a[1][1]=A.a[3][1]+1; A=(Fib^b)*A; printf("%lld ",A.a[1][0]); } return 0; }