题目大意:
给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种。将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。
无自环,可能有重边。
1<=n<=40,1<=m<=1000,1<=k<=10^18
分析:
将一个点拆成三个,然后将所有的边权拆成1,然后可以做矩阵乘法
为了方便计算我们加入一个计数点0,并且0向自己连个自环
那么矩阵的x次幂时的
∑
i
=
0
n
(
a
i
,
0
−
1
)
\sum_{i=0}^{n}(a_{i,0}-1)
∑i=0n(ai,0−1)就是长度
<
=
x
<=x
<=x时的方案数
那么我们可预处理出矩阵的
2
i
2^i
2i次幂时的方案数
然后去优化答案的统计
O
(
n
3
∗
65
+
n
3
l
o
g
2
k
)
O(n^3*65+n^3log_2k)
O(n3∗65+n3log2k)
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)
#define mt(x) memset(x, 0, sizeof(x))
#define mp(x, y) memcpy(x, y, sizeof(y))
#define N 1005
#define M 45
using namespace std;
typedef long long ll;
struct Matrix {
ll a[M*3][M*3];
};
Matrix C[65], cc, cur, tmp;
int id[M][3], n, m, cnt, R;
ll K, ans, now;
Matrix operator * (Matrix &aa, Matrix &bb) {
mt(cc.a);
rep(i, 0, 3 * n)
rep(j, 0, 3 * n)
rep(k, 0, 3 * n) cc.a[i][j] += aa.a[i][k] * bb.a[k][j];
return cc;
}
bool check(Matrix &aa) {
ll p = 0;
rep(i, 1, n) {
p += (aa.a[i][0] - 1);
if (K <= p) return 1;
}
return 0;
}
int main() {
scanf("%d %d %lld", &n, &m, &K);
rep(i, 1, n) C[0].a[i][0] = C[0].a[i][i + n] = C[0].a[i + n][i + 2 * n] = 1;
int u, v, w;
rep(i, 1, m)
scanf("%d %d %d", &u, &v, &w), ++C[0].a[u + (w - 1) * n][v];
C[0].a[0][0] = 1;
int R = 1;
for (R; R <= 66; R++) {
if (R > 65) { printf("-1\n"); return 0; }
C[R] = C[R - 1] * C[R - 1];
if (check(C[R])) break;
}
rep(i, 1, n) cur.a[i][i] = 1;
for(R--; ~R; --R) {
tmp = cur * C[R];
if (!check(tmp)) cur = tmp , ans += (1ll << R);
}
printf("%lld\n", ans);
return 0;
}