题目链接:http://poj.org/problem?id=3233
解题报告:输入一个边长为n的矩阵A,然后输入一个k,要你求A + A^2 + A^3 + A^4 + A^5.......A^k,然后结果的每个元素A[i][j] % m。(n <= 30,k < 10^9,m < 10^4)
要用到矩阵快速幂,但我认为最重要的其实还是相加的那个过程,因为k的范围是10^9,一个一个加肯定是不行的,我想了一个办法就是我以k = 8为例说明:
ans = A + A^2 + A^3 + A^4 + A^5 + A^6 + A^7 + A^8
= A + A^2 + A^3 + A^4 + A^4 * (A + A^2 + A^3 + A^4) // 这样分成两块之后就只要算一次A + A^2 + A^3 + A^4,就可以得出最后结果,
而A + A^2 + A^3 + A^4这个又可以通过相同的方法划分成如下:
= A + A^2 + A^2 * (A + A^2)同理。。。。就可以在logn时间求出他们的和了,然后快速的求A^k次方是用二分矩阵快速幂这里就不说了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<deque>
#include<cmath>
using namespace std;
typedef __int64 INT;
const int N = ;
int m; struct node
{
INT t[N][N];
int n;
friend node operator * (node a,node b)
{
node B = a;
for(int i = ;i < a.n;++i)
for(int j = ;j < a.n;++j)
{
int tot = ;
for(int k = ;k < a.n;++k)
tot += ((a.t[i][k] * b.t[k][j]) % m);
B.t[i][j] = tot % m;
}
return B;
}
friend node operator + (node a,node b)
{
node B;
B.n = a.n;
for(int i = ;i < a.n;++i)
for(int j = ;j < a.n;++j)
B.t[i][j] = (a.t[i][j] + b.t[i][j]) % m;
return B;
}
};
node A;
void print(node t)
{
for(int i = ;i < t.n;++i)
for(int j = ;j < t.n;++j)
printf(j == t.n-? "%d\n":"%d ",t.t[i][j]);
}
node Pw(node tt,int n) //二分矩阵快速幂求A ^ n
{
if(n == ) return A;
node res;
res.n = tt.n;
memset(res.t,,sizeof(res.t));
for(int i = ;i < res.n;++i)
res.t[i][i] = ;
while(n)
{
if(n & )
res = res * tt;
n >>= ;
tt = tt * tt;
}
return res;
} node get_ans(int n) //求A^1 到 A ^ n的和
{
if(n == )
return A;
if(n & )
{
node temp1;
temp1.n = A.n;
temp1 = Pw(A,n);
node temp2;
temp2.n = ;
temp2 = get_ans(n-);
temp1 = temp1 + temp2;
return temp1;
}
else
{
node temp1;
temp1.n = A.n;
temp1 = Pw(A,n / );
node temp2 = get_ans(n / );
temp2.n = A.n;
temp1 = temp1 * temp2;
temp1 = temp1 + temp2;
return temp1;
}
} int main()
{ int n,k;
while(scanf("%d%d%d",&n,&k,&m)!=EOF)
{
A.n = n;
for(int i = ;i < n;++i)
for(int j = ;j < n;++j)
scanf("%d",&A.t[i][j]);
node ans = get_ans(k);
print(ans);
}
return ;
}