题目: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 $使用矩阵快速幂可以快速计算。

 #include <iostream>
#include <cstdio>
using namespace std;
const int maxN=;
int MOD;
struct Matrix{int n,a[maxN][maxN];}A,E;
Matrix operator+(Matrix A,Matrix B){
Matrix RT{};RT.n=A.n;
for(int i=;i<RT.n;++i)
for(int j=;j<RT.n;++j)
RT.a[i][j]=(A.a[i][j]+B.a[i][j])%MOD;
return RT;
}
Matrix operator*(Matrix A,Matrix B){
Matrix RT{};RT.n=A.n;
for(int i=;i<A.n;++i)
for(int j=;j<A.n;++j)
for(int k=;k<A.n;++k)
(RT.a[i][j]+=A.a[i][k]*B.a[k][j])%=MOD;
return RT;
}
Matrix operator^(Matrix A,int n){
Matrix RT=E;
for(;n;n>>=){
if(n&)RT=RT*A;
A=A*A;
}
return RT;
}
Matrix Sum(Matrix &A,int n){
if(n==)return A;
if(n&)return (A^n) + Sum(A,n-);
Matrix B=Sum(A,n>>);
return B+((A^(n>>))*B);
}
int main(){
for(int n,k;~scanf("%d %d %d",&n,&k,&MOD);){
A.n=E.n=n;
for(int i=;i<n;++i)
for(int j=;j<n;++j){
scanf("%d",&A.a[i][j]);
A.a[i][j]%=MOD;E.a[i][j]=;
}
for(int i=;i<n;++i)E.a[i][i]=;
Matrix RT=Sum(A,k);
for(int i=;i<n;++i){
for(int j=;j<n;++j)
printf("%d ",RT.a[i][j]);
puts("");
}
}
return ;
}

方法二:对于多项式,有秦九韶算法,而对$ 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] $

 #include <iostream>
#include <cstdio>
using namespace std;
const int maxN=;
int MOD;
struct Matrix{int n,a[maxN][maxN];}A[],B[];
Matrix operator+(Matrix A,Matrix B){
Matrix RT{};RT.n=A.n;
for(int i=;i<RT.n;++i)
for(int j=;j<RT.n;++j)
RT.a[i][j]=(A.a[i][j]+B.a[i][j])%MOD;
return RT;
}
Matrix operator*(Matrix A,Matrix B){
Matrix RT{};RT.n=A.n;
for(int i=;i<A.n;++i)
for(int j=;j<A.n;++j)
for(int k=;k<A.n;++k)
(RT.a[i][j]+=A.a[i][k]*B.a[k][j])%=MOD;
return RT;
}
int main(){
Matrix E{};
for(int n,k;~scanf("%d %d %d",&n,&k,&MOD);){
A[].n=E.n=n;
for(int i=;i<n;++i)
for(int j=;j<n;++j){
scanf("%d",&A[].a[i][j]);
A[].a[i][j]%=MOD;E.a[i][j]=;
}
for(int i=;i<n;++i)E.a[i][i]=;
for(int i=;k>>i;++i)A[i]=A[i-]*A[i-];
B[]=A[];
for(int i=;k>>i;++i)B[i]=B[i-]*(A[i-]+E);
Matrix RT{};RT.n=n;
for(int i=;~i;--i)if(k>>i&)RT=RT*A[i]+B[i];
for(int i=;i<n;++i){
for(int j=;j<n;++j)
printf("%d ",RT.a[i][j]);
puts("");
}
}
return ;
}

优化一下常数(141s -> 32s):

 #include <iostream>
#include <cstdio>
using namespace std;
const int maxN=;
int MOD;
struct Matrix{int n,a[maxN][maxN];}A[],B[];
Matrix operator+(Matrix A,Matrix B){
Matrix RT{};RT.n=A.n;
for(int i=;i<RT.n;++i)
for(int j=;j<RT.n;++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{};RT.n=A.n;
for(int i=;i<A.n;++i)
for(int j=;j<A.n;++j){
for(int k=;k<A.n;++k)
RT.a[i][j]+=A.a[i][k]*B.a[k][j];
RT.a[i][j]%=MOD;
}
return RT;
}
int main(){
Matrix E{};
for(int n,k;~scanf("%d %d %d",&n,&k,&MOD);){
A[].n=E.n=n;
for(int i=;i<n;++i)
for(int j=;j<n;++j){
scanf("%d",&A[].a[i][j]);
A[].a[i][j]%=MOD;E.a[i][j]=;
}
for(int i=;i<n;++i)E.a[i][i]=;
for(int i=;k>>i;++i)A[i]=A[i-]*A[i-];
B[]=A[];
for(int i=;k>>i;++i)B[i]=B[i-]*(A[i-]+E);
Matrix RT{};RT.n=n;
for(int i=;~i;--i)if(k>>i&)RT=RT*A[i]+B[i];
for(int i=;i<n;++i){
for(int j=;j<n;++j)
printf("%d ",RT.a[i][j]);
puts("");
}
}
return ;
}

方法三:有种很有意思的打法:构造矩阵

$ 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] $

 #include <iostream>
#include <cstdio>
using namespace std;
const int maxN=;
int MOD;
struct Matrix{int n,a[maxN][maxN];}A;
Matrix operator+(Matrix A,Matrix B){
Matrix RT{};RT.n=A.n;
for(int i=;i<RT.n;++i)
for(int j=;j<RT.n;++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{};RT.n=A.n;
for(int i=;i<A.n;++i)
for(int j=;j<A.n;++j){
for(int k=;k<A.n;++k)
RT.a[i][j]+=A.a[i][k]*B.a[k][j];
RT.a[i][j]%=MOD;
}
return RT;
}
Matrix Sum(Matrix A,int k){
Matrix RT{};int n=RT.n=A.n;
for(int t=n/,i=;i<n;++i)RT.a[i][i+t]=;
for(;k;k>>=){
if(k&)RT=RT*A;
A=A*A;
}
return RT;
}
int main(){
for(int n,k;~scanf("%d %d %d",&n,&k,&MOD);){
A.n=n<<;
for(int i=;i<n;++i){
A.a[i][i]=;
for(int j=,t;j<n;++j){
scanf("%d",&t);if(t>=MOD)t%=MOD;
A.a[i+n][j]=A.a[i+n][j+n]=t;
}
}
Matrix RT=Sum(A,k);
for(int i=;i<n;++i){
for(int j=;j<n;++j)
printf("%d ",RT.a[i][j]);
puts("");
}
}
return ;
}

题目: Gauss Fibonacci

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1588

分析:Fibonacci数列可用矩阵:$ Fib =  \left[ \begin{array}{cc} 0 & 1 \\ 1  & 1 \end{array} \right] $ 表示,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) $

就和上面一样了。

 #include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const int maxN=;
LL MOD;
struct Matrix{LL a[maxN][maxN];}A[],B[],E,Fib;
Matrix operator+(Matrix A,Matrix B){
Matrix RT{};
for(int i=;i<;++i)
for(int j=;j<;++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{};
for(int i=;i<;++i)
for(int j=;j<;++j){
for(int k=;k<;++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=E;
for(;n;n>>=){
if(n&)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.a[][]=;Fib.a[][]=;
Fib.a[][]=;Fib.a[][]=;
E.a[][]=E.a[][]=;
E.a[][]=E.a[][]=; A[]=Fib^k;
for(int i=;n>>i;++i)A[i]=A[i-]*A[i-];
B[]=A[];
for(int i=;n>>i;++i)B[i]=B[i-]*(A[i-]+E); Matrix RT{};
for(int i=;~i;--i)if(n>>i&)RT=RT*A[i]+B[i];
RT=(RT+E)*(Fib^b);
printf("%lld\n",RT.a[][]);
}
return ;
}
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const int maxN=;
LL MOD;
struct Matrix{int n;LL a[maxN][maxN];}A,Fib;
Matrix operator+(Matrix A,Matrix B){
Matrix RT{};
for(int i=;i<;++i)
for(int j=;j<;++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{};int n=RT.n=A.n;
for(int i=;i<n;++i)
for(int j=;j<n;++j){
for(int k=;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=;i<RT.n;++i)for(int j=;j<RT.n;++j)RT.a[i][j]=;
for(int i=;i<RT.n;++i)RT.a[i][i]=;
for(;n;n>>=){
if(n&)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=;
Fib.a[][]=;Fib.a[][]=;
Fib.a[][]=;Fib.a[][]=; A=Fib^k;
A.n=;
A.a[][]=A.a[][]=A.a[][];
A.a[][]=A.a[][]=A.a[][];
A.a[][]=A.a[][]=A.a[][];
A.a[][]=A.a[][]=A.a[][];
A.a[][]=;A.a[][]=;
A.a[][]=;A.a[][]=; A=A^n;
A.n=;
A.a[][]=A.a[][]+;A.a[][]=A.a[][]+;
A.a[][]=A.a[][]+;A.a[][]=A.a[][]+; A=(Fib^b)*A;
printf("%lld\n",A.a[][]);
}
return ;
}
05-20 20:18