题面:[SCOI2009] 迷路

大秦为你打开题目传送门

题目背景

windy 在有向图中迷路了。

题目描述

该有向图有 $n$ 个节点,节点从 $1$ 至 $n$ 编号,windy 从节点 $1$ 出发,他必须恰好在 $t$ 时刻到达节点 $n$。

现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?

答案对 $2009$ 取模。

注意:windy 不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

输入格式

第一行包含两个整数,分别代表 $n$ 和 $t$。

第 $2$ 到第 $(n + 1)$ 行,每行一个长度为 $n$ 的字符串,第 $(i + 1)$ 行的第 $j$ 个字符 $c_{i, j}$ 是一个数字字符,若为 $0$,则代表节点 $i$ 到节点 $j$ 无边,否则代表节点 $i$ 到节点 $j$ 的边的长度为 $c_{i, j}$。

输出格式

输出一行一个整数代表答案对 $2009$ 取模的结果。

样例 #1

样例输入 #1

1
2
3
2 2
11
00

样例输出 #1

1
1

样例 #2

样例输入 #2

1
2
3
4
5
6
5 30
12045
07105
47805
12024
12345

样例输出 #2

1
852

提示

样例输入输出 1 解释

路径为 $1 \to 1 \to 2$。

数据规模与约定

  • 对于 $30\%$ 的数据,保证 $n \leq 5$,$t \leq 30$。
  • 对于 $100\%$ 的数据,保证 $2 \leq n \leq 10$,$1 \leq t \leq 10^9$。

思路

首先,我们考虑路径只有 01 怎么做,不难想到用类似于 Floyd 的做法写出下面的递推式,设 $dp[t][u][v]$ 表示 $u$ 点和 $v$ 点在 $t$ 时刻到达的路径的方案数。

不难发现,这个递推式非常的相似于矩阵快速幂的计算公式。如果我们把这个 DP 写成矩阵的形式,也就是说我们令每一个 $dp[t]$ 表示 $t$ 时刻任意两点之间的方案数,那么可以得到递推式:

这样就可以用矩阵快速幂求解,这个是路径 01 的做法,那么我们考虑如何把题目转化。由于发现题目给的一个明显性质,每一个边权是 $0\sim 9$ 的数字,我们是不是可以考虑把一条路径拆成若干条长度为 1 的边。这样就可以了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef __int128 int128;
typedef pair<int , int > pii;
typedef unsigned long long ull;

namespace FastIO
{
// char buf[1 << 20], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)
template<typename T> inline T read(T& x) {
x = 0;
int f = 1;
char ch;
while (!isdigit(ch = getchar())) if (ch == '-') f = -1;
while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
x *= f;
return x;
}
template<typename T, typename... Args> inline void read(T& x, Args &...x_) {
read(x);
read(x_...);
return;
}
inline ll read() {
ll x;
read(x);
return x;
}
};
using namespace FastIO;

const int P = 2009;

class Matrix {
public:
int n, m;
vector<vector<ll > > mat;

Matrix(int n, int m) : n(n), m(m), mat(n, vector<ll >(m, 0)) {}

Matrix operator+(const Matrix& B) const {
if(n != B.n || m != B.m) {
return *this;
}
Matrix result(n, m);
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
result.mat[i][j] = (mat[i][j] + B.mat[i][j]) % P;
}
}
return result;
}

Matrix operator*(const Matrix& B) const {
if(m != B.n) {
return *this;
}
Matrix result(n, B.m);
for(int i = 0; i < n; ++i) {
for(int j = 0; j < B.m; ++j) {
result.mat[i][j] = 0;
for(int k = 0; k < m; ++k) {
result.mat[i][j] = (result.mat[i][j] + mat[i][k] * B.mat[k][j] % P) % P;
}
}
}
return result;
}

Matrix power(int p) const {
if(n != m) {
return *this;
}
Matrix result(n, n);
for(int i = 0; i < n; ++i) {
result.mat[i][i] = 1;
}
Matrix base = *this;
while(p > 0) {
if(p % 2 == 1) {
result = result * base;
}
base = base * base;
p /= 2;
}
return result;
}
friend ostream& operator<<(ostream& os, const Matrix& A) {
for (int i = 0; i < A.n; ++i) {
for (int j = 0; j < A.m; ++j) {
os << A.mat[i][j] << " ";
}
os << endl;
}
return os;
}
};

int n, m, s;

Matrix mp(0, 0);
int pos(int u, int a) { return u + a * n - 1; }

inline void Input() {
read(n, s);
mp = Matrix(9 * n, 9 * n);
for(int i = 1; i <= n; i++) {
for(int j = 1; j < 9; j++) {
mp.mat[pos(i, j)][pos(i, j - 1)] = 1;
}
}
int w;
for(int u = 1; u <= n; u++) {
for(int v = 1; v <= n; v++) {
scanf("%1d", &w);
if(w != 0) mp.mat[u - 1][pos(v, w - 1)] = 1;
}
}
}

inline void Work() {
// cout << mp.power(s) << endl;
printf("%lld\n", mp.power(s).mat[0][n - 1]);
}

int main() {
int T = 1;
while(T--) {
Input();
Work();
}
return 0;
}