第一道自己写出来的紫题,写篇题解纪念一下。
题意简述
给出一张带权图 ( w i ≤ 10 w_i \le 10 wi≤10),求定长路径方案数。
解法
我们已经会了如何求无权图定长路径方案数,但这道题是带权图。
通过一点点的直觉,我们可以猜到本题的突破口和小的离谱
w
i
w_i
wi 有关。
在经过一点点的思考,我们就知道了可以把每个点拆成十个点,这样就可以看作是一张边权为
1
1
1 的图了。
更具体的,我们可以先将每个点拆成的十个点全部连上边权为
1
1
1 的边,然后再将每个拆成点中第
w
i
−
1
w_i-1
wi−1 向目标点连边。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 20;
const int mod = 2009;
typedef long long ll;
char s[15][15];
struct Matrix {
int n, m;
int a[maxn][maxn];
Matrix() { memset(a, 0, sizeof a); }
Matrix(int _n, int _m) {
n = _n;
m = _m;
memset(a, 0, sizeof a);
}
};
Matrix mul(Matrix &a, Matrix &b) {
Matrix res(a.n, b.m);
for (int i = 0; i < a.n; i++) {
for (int j = 0; j < b.m; j++) {
for (int k = 0; k < a.m; k++) {
(res.a[i][j] += a.a[i][k] * b.a[k][j] % mod) %= mod;
}
}
}
return res;
}
Matrix Pow(Matrix &base, int n) {
Matrix res(base.n, base.n);
for(int i = 0; i < res.n; i ++) res.a[i][i] = 1;
while (n != 0) {
if (n & 1) res = mul(res, base);
base = mul(base, base);
n >>= 1;
}
return res;
}
int main() {
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) scanf("%s", s[i]);
Matrix g(n * 10, n * 10);
for (int i = 0; i < n; i++){
for (int j = 0; j < 9; j++){
g.a[i * 10 + j][i * 10 + j + 1] = 1;
}
}
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (s[i][j] - '0'){
int cnt=s[i][j]-'0';
g.a[i * 10 + cnt - 1][j * 10] = 1;
}
}
}
g = Pow(g, k);
cout << g.a[0][n * 10 - 10];
return 0;
}