d769: 共有多少種走法?
Tags: fast-exponentiation, graph, matrix
出處:https://zerojudge.tw/ShowProblem?problemid=d769
提交:https://zerojudge.tw/Submissions?problemid=d769&account=allllllan123456
問題:給你一張有 $N\in[1,50]$ 個點的單向無權圖 (編號 1 到 $N$),其中的每個點 $x$ 都有 $s_{x,y}\in[0,20]$ 條邊可以抵達 $y$,根據這個定義,一條邊可以連接同一個頂點,同時也有多條邊有相同的起訖點。現在固定常數 $k\in[1,10^7]$,接下來有 $Q\le N^2$ 筆詢問,每次問你從點 i 出發剛好走過 $k$ 條邊之後抵達點 j 的方法數有幾種。
解法:這題 AC 率高,應該是因為解法蠻經典的。如果一個矩陣 $M_{k_1}$ 的第 i 列第 j 行表示從 i 出發剛好走過 $k_1$ 條邊之後抵達 j 的方法數,而另一個矩陣 $M_{k_2}$ 的第 i 列第 j 行表示從 i 出發剛好走過 $k_2$ 條邊之後抵達 j 的方法數,那麼矩陣乘法 $M_{k_1+k_2} = M_{k_1}\cdot M_{k_2}$ 的第 i 列第 j 行就表示從 i 出發剛好走過 $k_1 + k_2$ 條邊後抵達 j 的方法數。每次詢問的答案便是 $M_k=(M_1)^{k}$ 的第 i 列第 j 行,這樣就能用快速冪了。
1#include <bits/stdc++.h>
2using namespace std;
3
4struct matrix {
5 int n, e[51][51] = {0};
6 matrix(int size, bool unit=false) : n(size) {
7 if (unit)
8 for (int i=1; i<=n; i++)
9 e[i][i] = 1;
10 }
11 matrix operator*(const matrix& m2) const {
12 matrix ans(n);
13 for (int i=1; i<=n; i++)
14 for (int j=1; j<=n; j++) {
15 for (int k=1; k<=n; k++)
16 ans.e[i][j] += e[i][k] * m2.e[k][j];
17 ans.e[i][j] %= 5281;
18 }
19 return ans;
20 }
21};
22
23int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); // IO 優化
24 int N, k;
25 while (cin >> N >> k) {
26 matrix m(N); int x, y, s;
27 while (cin >> x >> y >> s && (x || y || s))
28 m.e[x][y] = s;
29 matrix ans(N, true);
30 while (k) {
31 if (k & 1)
32 ans = ans * m;
33 m = m * m, k >>= 1;
34 }
35 int Q; cin >> Q;
36 while (Q--)
37 cin >> x >> y, cout << ans.e[x][y] << '\n';
38 }
39 return 0;
40}