[題目][藍橋杯ALGO-60] 矩陣乘方

一、題目

0、題目鏈接

http://lx.lanqiao.cn/problem.page?gpid=T104(需要登錄且需要 VIP 賬戶)

1、問題描述

給定一個矩陣 A,一個非負整數 b 和一個正整數 m,求 A 的 b 次方除 m 的餘數。

其中一個 n x n 的矩陣除 m 的餘數得到的仍是一個 n x n 的矩陣,這個矩陣的每一個元素是原矩陣對應位置上的數除m的餘數。

要計算這個問題,可以將 A 連乘 b 次,每次都對 m 求餘,但這種方法特別慢,當b較大時無法使用。下面給出一種較快的算法(用 A ^ b 表示 A 的 b 次方):

若 b = 0,則 A ^ b % m = I % m。其中 I 表示單位矩陣。

若 b 爲偶數,則 A ^ b % m = (A ^ (b / 2) % m) ^ 2 % m,即先把 A 乘 b / 2 次方對 m 求餘,然後再平方後對 m 求餘。

若 b 爲奇數,則 A ^ b % m = (A ^ (b - 1) % m) * a % m,即先求A乘 b - 1 次方對 m 求餘,然後再乘 A 後對 m 求餘。

這種方法速度較快,請使用這種方法計算 A ^ b % m ,其中 A 是一個 2 x 2 的矩陣,m 不大於 10000。

2、輸入格式

輸入第一行包含兩個整數 b, m,第二行和第三行每行兩個整數,爲矩陣 A。

3、輸出格式

輸出兩行,每行兩個整數,表示 A ^ b % m 的值。

4、樣例輸入

2 2
1 1
0 1

5、樣例輸出

1 0
0 1

 

二、分析與思路

有點意思的一道基礎題。

首先這是個矩陣乘法問題,結合線性代數的知識,不難理解這個過程,並且這道題的矩陣限定爲 2 * 2,就更簡單了。題面給出了較快的算法,然而我完全沒 get 到它想表達的,於是真就直接對 b 判斷一次奇偶再分別處理,可能是藍橋杯 OJ 裏的 sb 題太多了,以爲這道題也很 sb,交了一發輕鬆爆 0。

然後乾脆忽略掉了這個較快方法,又交了一發直接求解,90 分,最後一個點竟然 TLE,下載數據一看 b > 10 ^ 8,再回頭看題,原來壓根就沒給出 b 的數據範圍。

大致看了下其他的博客,重新讀了遍題,才意識到題面所謂的較快方法 —— 原來是在暗示快速冪。。甚至可以說是明示。題目那兩句話本質是隱含了遞歸思想的,確實之前沒做過矩陣快速冪,完全沒有往這方面想。

那麼明白了需要快速冪之後,其實矩不矩陣是沒啥影響的,把矩陣的乘法運算封裝一下,和普通的快速冪是幾乎一樣的。

 

三、代碼

1、常規代碼

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 
 6 ll n, m, c[3][3], a[3][3], res[3][3];
 7 
 8 void multi(ll a[][3], ll b[][3]) {
 9     memset(c, 0, sizeof(c));
10      for (int i = 1; i <= 2; i++)
11         for (int j = 1; j <= 2; j++)
12             for (int k = 1; k <= 2; k++) 
13                 (c[i][j] += a[i][k] * b[k][j]) %= m;
14     for (int i = 1; i <= 2; i++)
15         for (int j = 1; j <= 2; j++)
16             a[i][j] = c[i][j];
17 }
18 
19 int main() {
20     cin >> n >> m;
21     cin >> a[1][1] >> a[1][2] >> a[2][1] >> a[2][2];
22     if (n) {
23         res[1][1] = a[1][1] % m, res[1][2] = a[1][2] % m;
24         res[2][1] = a[2][1] % m, res[2][2] = a[2][2] % m;
25         n--;
26         while (n) {
27             if (n & 1) multi(res, a);
28             multi(a, a);
29             n >>= 1;
30         }
31         cout << res[1][1] << ' ' << res[1][2] << endl;
32         cout << res[2][1] << ' ' << res[2][2];
33     }
34     else cout << "0 0\n0 0";
35     return 0;
36 }

2、矩陣封裝類代碼

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 
 6 ll n, m, i, j, k, l;
 7 
 8 class Matrix {
 9 public:
10     ll a[2][2];
11     Matrix() {}
12     Matrix(int i, int j, int k, int l):
13         a({{i, j}, {k, l}}) {}
14     friend Matrix operator * (const Matrix x, const Matrix y) {
15         Matrix t;
16         t.a[0][0] = (x.a[0][0] * y.a[0][0] + x.a[0][1] * y.a[1][0]) % m;
17         t.a[0][1] = (x.a[0][0] * y.a[0][1] + x.a[0][1] * y.a[1][1]) % m;
18         t.a[1][0] = (x.a[1][0] * y.a[0][0] + x.a[1][1] * y.a[1][0]) % m;
19         t.a[1][1] = (x.a[1][0] * y.a[0][1] + x.a[1][1] * y.a[1][1]) % m;
20         return t;
21     }
22 };
23  
24 int main() {
25     cin >> n >> m;
26     cin >> i >> j >> k >> l;
27     Matrix a(i, j, k, l), res;
28     if (n) {
29         res = a;
30         n--;
31         while (n) {
32             if (n & 1) res = res * a;
33             a = a * a;
34             n >>= 1;
35         }
36         cout << res.a[0][0] % m << ' ' << res.a[0][1] % m << endl;
37         cout << res.a[1][0] % m << ' ' << res.a[1][1] % m ;
38     }
39     else cout << "0 0\n0 0";
40     return 0;
41 }

 

四、相關知識點

6.2  快速冪

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章