poj 3233 Matrix Power Series(矩阵快速幂)

Description

Given a n × n matrix A and a positive integer k, find the sum S = A + A2 + A3 + … + Ak.

Input

The input contains exactly one test case. The first line of input contains three positive integers n (n ≤ 30), k (k ≤ 109) and m (m < 104). Then follow n lines each containing n nonnegative integers below 32,768, giving A’s elements in row-major order.

Output

Output the elements of S modulo m in the same way as A is given.

Sample Input

2 2 4
0 1
1 1

Sample Output

1 2
2 3
解题思路:等比矩阵求和,采用递归二分的方法。

上图就给出了求前k项等比矩阵的和的递推式,解法采用递归来求和比较直观,也很好理解。
AC代码(1704ms):
 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 const int maxn=35;
 5 int n,k,mod;
 6 struct Matrix{int m[maxn][maxn];}init;
 7 Matrix add(Matrix a,Matrix b){//矩阵加法
 8     Matrix c;
 9     for(int i=0;i<n;++i)
10         for(int j=0;j<n;++j)
11             c.m[i][j]=(a.m[i][j]+b.m[i][j])%mod;
12     return c;
13 }
14 Matrix mul(Matrix a,Matrix b){//矩阵乘法
15     Matrix c;
16     for(int i=0;i<n;i++){
17         for(int j=0;j<n;j++){
18             c.m[i][j]=0;
19             for(int k=0;k<n;k++)
20                 c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
21         }
22     }
23     return c;
24 }
25 Matrix quick_power(Matrix a,int x){//矩阵快速幂
26     Matrix b;memset(b.m,0,sizeof(b.m));
27     for(int i=0;i<n;++i)b.m[i][i]=1;//单位矩阵
28     while(x){
29         if(x&1)b=mul(b,a);
30         a=mul(a,a);x>>=1;
31     }
32     return b;
33 }
34 Matrix sum(Matrix s,int k){//求前k项和
35     if(k==1)return s;//递归出口:当幂指数为1时,返回当前的矩阵
36     Matrix tmp;memset(tmp.m,0,sizeof(tmp.m));//暂存保存中间的矩阵
37     for(int i=0;i<n;++i)tmp.m[i][i]=1;//单位矩阵
38     tmp=add(tmp,quick_power(s,k>>1));//计算1+A^{k/2}
39     tmp=mul(tmp,sum(s,k>>1));//计算(1+A^{k/2})*(A + A^2 + A^3 + … + A^{k/2})=(1+A^{k/2})*(S_{k/2})
40     if(k&1)tmp=add(tmp,quick_power(s,k));//若k是奇数,则加上A^k
41     return tmp;//返回前k项的值
42 }
43 void print_rectangle(Matrix r){
44     for(int i=0;i<n;++i)
45         for(int j=0;j<n;++j)
46             cout<<r.m[i][j]<<((j==n-1)?"\n":" ");
47 }
48 int main(){
49     while(cin>>n>>k>>mod){
50         for(int i=0;i<n;++i)
51             for(int j=0;j<n;++j)
52                 cin>>init.m[i][j];
53         init=sum(init,k);
54         print_rectangle(init);
55     }
56     return 0;
57 }

 采用分块矩阵求解可以减少很多时间,具体推导可以结合下图:

矩阵中套矩阵,效率上比上一种方法更快,实际上是2n×2n的矩阵快速幂。就样例展开等式左边的矩阵:

那么最终的答案就是等式右边矩阵中左下角的子矩阵减去单位矩阵,注意某个元素值减-1之后可能为-1,那么此时只需再加上mod即可。

AC代码(610ms):

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 const int maxn=70;
 5 struct Matrix{int m[maxn][maxn];}init;
 6 int n,k,mod;
 7 Matrix mul(Matrix a,Matrix b){//矩阵乘法
 8     Matrix c;
 9     for(int i=0;i<2*n;i++){//矩阵大小扩大两倍,枚举第一个矩阵的行
10         for(int j=0;j<2*n;j++){//枚举第二个矩阵的列
11             c.m[i][j]=0;
12             for(int k=0;k<2*n;k++)//枚举第一个矩阵的列
13                 c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
14         }
15     }
16     return c;
17 }
18 Matrix POW(Matrix a,int x){//矩阵快速幂
19     Matrix b;memset(b.m,0,sizeof(b.m));
20     for(int i=0;i<2*n;++i)b.m[i][i]=1;//构造单位矩阵
21     while(x){
22         if(x&1)b=mul(b,a);
23         a=mul(a,a);x>>=1;
24     }
25     return b;
26 }
27 void print_rectangle(Matrix r){
28     for(int i=0;i<n;++i){
29         for(int j=0;j<n;++j){
30             if(i==j)r.m[n+i][j]-=1;
31             if(r.m[n+i][j]<0)r.m[n+i][j]+=mod;//左下角子矩阵减去单位矩阵,减完可能小于0,因此需要加上mod再取余mod
32             cout<<r.m[n+i][j]<<((j==n-1)?'\n':' ');
33         }
34     }
35 }
36 int main(){
37     while(cin>>n>>k>>mod){
38         memset(init.m,0,sizeof(init.m));//全部初始化为0
39         for(int i=0;i<n;++i){//首先初始化矩阵
40             for(int j=0;j<n;++j)
41                 cin>>init.m[i][j];
42             init.m[n+i][i]=init.m[n+i][n+i]=1;//其余是单位矩阵
43         }
44         init=POW(init,k+1);//求其前k+1项和(左下角包含单位矩阵,最后输出要减去单位矩阵)
45         print_rectangle(init);//打印等比矩阵A前k项和
46     }
47     return 0;
48 }

 

posted @ 2018-05-24 14:41  霜雪千年  阅读(275)  评论(0编辑  收藏  举报