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次方和。
解題思路:該題可以有兩種解題思路,一是通過矩陣遞推式,直接通過狀態轉移矩陣加快矩陣變化,推出最終結果。二是通過二分的思想解決。
一、關於遞推,k最大可以達到1e9,那麼考慮用矩陣快速冪求解。先要確定這初始矩陣是
,再根據遞推式,得到狀態轉移矩陣是,這樣我們就可以通過狀態轉移矩陣的n-1次方再乘以初始矩陣就是我們所要求的目標矩陣,因爲我選用的初始矩陣是,所以狀態轉移矩陣就需要n-1次方,如果初始矩陣不同,那麼轉移矩陣所需要轉移的次數也會不同。而通過這題,我們可以非常明顯的看到在運用矩陣快速冪的時候,我們需要確定的有兩個量,一個是初始矩陣,另一個是狀態轉移矩陣,而使用矩陣快速冪,使狀態轉移矩陣轉移的更快。
二、關於二分主要是推出
當k爲偶數時不需要加 A^k,因爲剛好二分完,當k爲奇數時,因爲k/2是向下取整,會掉了最後的A^k,所以要補回來。
關於代碼,矩陣相乘非常好寫,而矩陣快速冪就是在快速冪的基礎上將數的相乘轉換爲了矩陣相乘,也是相當容易的。別忘了取模。
代碼:
方法一:
//對狀態轉移矩陣用矩陣快速冪加速
#include <iostream>
#include <cstring>
#define ll long long
using namespace std;
ll N, Mod;
struct Matrix
{
int m[61][61];
};
Matrix multi(Matrix a, Matrix b)
{
Matrix ans;
memset(ans.m, 0, sizeof(ans.m));
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
for(int k = 1; k <= N; k++)
ans.m[i][j] = (ans.m[i][j] + a.m[i][k]*b.m[k][j] % Mod) % Mod;
return ans;
}
Matrix pow(Matrix a, ll n)
{
Matrix ans;
memset(ans.m, 0, sizeof(ans.m));
for(int i = 1; i <= N; i++)
ans.m[i][i] = 1;
while(n)
{
if(n&1)
ans = multi(ans, a);
n >>= 1;
a = multi(a, a);
}
return ans;
}
int main()
{
Matrix ans, T;
ll n, k, data;
cin >> n >> k >> Mod;
N = n*2;
memset(ans.m, 0, sizeof(ans.m));
memset(T.m, 0, sizeof(T.m));
for(int i = 1; i <= n; i++)
{
ans.m[i][i] = 1;
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
{
cin >> data;
ans.m[i][j+n] = data % Mod;
ans.m[i+n][j+n] = data % Mod;
T.m[i][j] = data % Mod;
T.m[i+n][j] = data % Mod;
}
// for(int i = 1; i <= N; i++)
// {
// for(int j = 1; j < N; j++)
// {
// cout << T.m[i][j]<< ' ';
// }
// cout << T.m[i][N] << endl;
// }
ans = pow(ans, k-1);
ans = multi(ans, T);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j < n; j++)
{
cout << ans.m[i][j]<< ' ';
}
cout << ans.m[i][n] << endl;
}
return 0;
}
方法二:
因爲該方法是重新做該題時重新碼的代碼,可能風格會有不同。
//矩陣快速冪 + 二分思想
#include <iostream>
#include <cstring>
#include <cstdio>
#define LL long long
using namespace std;
LL Mod;
int N, K;
struct Matrix
{
LL m[35][35];
};
Matrix A, pr;
Matrix Multi(Matrix a, Matrix b)
{
Matrix ans;
memset(ans.m, 0, sizeof(ans.m));
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
{
for(int k = 1; k <= N; k++)
ans.m[i][j] = (ans.m[i][j] + a.m[i][k]*b.m[k][j])%Mod;
}
return ans;
}
Matrix Add(Matrix a, Matrix b)
{
Matrix ans;
memset(ans.m, 0, sizeof(ans.m));
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= N; j++)
{
ans.m[i][j] = (a.m[i][j] + b.m[i][j])% Mod;
}
}
return ans;
}
Matrix Power(Matrix a, int k)
{
Matrix ans = pr;
while(k)
{
if(k&1)
{
ans = Multi(ans, a);
}
k >>= 1;
a = Multi(a, a);
}
return ans;
}
Matrix solve(int k)
{
if(k == 1) return A;
Matrix b, temp = Power(A, k>>1);
temp = Add(temp, pr);
b = Multi(temp, solve(k>>1));
if(k&1)
{
b = Add(b, Power(A, k));
}
return b;
}
int main()
{
scanf("%d %d %I64d", &N, &K, &Mod);
memset(pr.m, 0, sizeof(pr.m));
for(int i = 1; i <= N; i++)
{
pr.m[i][i] = 1;
for(int j = 1; j <= N; j++)
{
scanf("%I64d", &A.m[i][j]);
}
}
Matrix ans = solve(K);
for(int i = 1; i <= N; i++)
{
printf("%I64d", ans.m[i][1]);
for(int j = 2; j <= N; j++)
{
printf(" %I64d", ans.m[i][j]);
}
printf("\n");
}
return 0;
}