题目描述
给定 n × n n×n n×n 矩阵 A A A 和正整数 k k k,求和 S = A + A 2 + A 3 + … + A k S=A+A^2+A^3+…+A^k S=A+A2+A3+…+Ak。
数据范围
1
≤
n
≤
30
,
1≤n≤30,
1≤n≤30,
1
≤
k
≤
1
0
9
,
1≤k≤10^9,
1≤k≤109,
1
≤
m
<
1
0
4
1≤m<10^4
1≤m<104
题解
本题中就没有什么隐含的递推式子了,直接裸着给出来了要求的值
这道题正解是用递归(分治)的方法来解决,但其实有一个巧妙而又神奇的方法,直接矩阵快速幂
我们构造出一个矩形:
[
A
E
0
E
]
2
=
[
A
2
A
+
E
0
E
]
\left[ \begin{matrix} A & E \\ 0 & E \end{matrix} \right]^2= \left[ \begin{matrix} A^2 & A+E \\ 0 & E \end{matrix} \right]
[A0EE]2=[A20A+EE]
E
E
E是指一个只有对角线(
i
=
j
i=j
i=j)的点才为
1
1
1,其他的点都为
0
0
0的初始化矩阵,这样当一个矩阵与其做乘法时,得到的矩阵依然为它自身
观察这个简化的矩阵,我们对其进行幂运算
[
A
E
0
E
]
2
=
[
A
2
A
+
E
0
E
]
\left[ \begin{matrix} A & E \\ 0 & E \end{matrix} \right]^2= \left[ \begin{matrix} A^2 & A+E \\ 0 & E \end{matrix} \right]
[A0EE]2=[A20A+EE]
[
A
E
0
E
]
3
=
[
A
3
A
2
+
A
+
E
0
E
]
\left[ \begin{matrix} A & E \\ 0 & E \end{matrix} \right]^3= \left[ \begin{matrix} A^3 & A^2+A+E \\ 0 & E \end{matrix} \right]
[A0EE]3=[A30A2+A+EE]
很容易可以观察出来,矩阵右上角的一项
−
E
-E
−E就是我们要求的最终的答案,也就是说,我们构造出这个矩阵,对它进行
(
m
+
1
)
(m+1)
(m+1)次方,得到的矩阵右上角的一部分
−
E
-E
−E即为所求
代码量应该是比递归写法要短不少的
注意:由于存在取模运算,所以得到的最后矩阵中可能有得点值恰好被模为0,当它又恰好在对角线上,要被减1时,就可能出现负数,所以要进行取模防负数
code
#include<bits/stdc++.h>
using namespace std;
int a[65][65],e[65][65];
int n,k,m;
void mul()
{
int c[65][65];
memset(c,0,sizeof(c));
for(int i=1;i<=2*n;i++)
for(int j=1;j<=2*n;j++)
for(int k=1;k<=2*n;k++)
c[i][j]=(c[i][j]+1ll*e[i][k]*a[k][j])%m;
memcpy(e,c,sizeof(c));
}
void mulself()
{
int c[65][65];
memset(c,0,sizeof(c));
for(int i=1;i<=2*n;i++)
for(int j=1;j<=2*n;j++)
for(int k=1;k<=2*n;k++)
c[i][j]=(c[i][j]+1ll*a[i][k]*a[k][j])%m;
memcpy(a,c,sizeof(c));
}
int main()
{
scanf("%d%d%d",&n,&k,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++) a[i][i+n]=a[i+n][i+n]=1;
memcpy(e,a,sizeof(a));
while(k)
{
if(k&1) mul();
k>>=1;
mulself();
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) printf("%d ",(e[i][j+n]-1+m)%m);
else printf("%d ",e[i][j+n]);
}
puts("");
}
return 0;
}