https://acm.hdu.edu.cn/showproblem.php?pid=7322
经典最小环问题。可以用floyd求解。在floyd中,三层循环k,i,j,此时利用k更新i->j的距离,那么如果j->i有边,那么就会构成环。所以d[i][j]表示i->j的最短距离,cnt[i][j]表示i->j最短路径的条数,d[i][j]表示初始状态下的i->j的边。同时,为了避免对一个环的重复计算,我们考虑在环的编号最大的结点去计算,这是由于floyd本身是个dp的做法,完整形式应该是dp[k][i][j],表示从i->j,由不超过k的结点更新的最短距离,也就是说,除了i,j以外,路径上的点不超过k。所以,只需要当枚举到最外层的k时,在枚举环上的另外一个与k相邻的结点即可,此时,这个环的所有结点全都不超过k。不重不漏。注意acwing的观光之旅这道题目,只需要考虑最小环的长度,不需要记录数量,所以,并没有真的按照在环上的最大节点去更新答案,会重复计算。
#include <bits/stdc++.h>
#define LL long long
#define int LL
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define x first
#define y second
#define kanm7 ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 510, mod = 998244353;
int g[N][N], cnt[N][N], d[N][N];
int n, m, ans = 1e18, res = 0;
void floyd() {
for(int k = 1; k <= n; k ++) {
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++){
if(g[i][k] + g[k][j] < g[i][j]) {
g[i][j] = g[i][k] + g[k][j];
cnt[i][j] = cnt[i][k] * cnt[k][j] % mod;
} else if(g[i][k] + g[k][j] == g[i][j]) {
cnt[i][j] += cnt[i][k] * cnt[k][j] % mod;
cnt[i][j] %= mod;
}
}
}
for(int i = 1; i < k; i ++) {
if(d[k][i]) {
if(d[k][i] + g[i][k] < ans) {
ans = d[k][i] + g[i][k];
res = cnt[i][k];
} else if(d[k][i] + g[i][k] == ans) {
res += cnt[i][k];
res %= mod;
}
}
}
}
}
void solve() {
cin >> n >> m;
ans = 1e18;
res = 0;
for(int i = 0; i <= n; i ++) {
for(int j = 0; j <= n; j ++) {
g[i][j] = 1e18;
cnt[i][j] = 0;
d[i][j] = 1e18;
}
g[i][i] = 0;
d[i][i] = 0;
}
while(m --) {
int a, b, c; cin >> a >> b >> c;
g[a][b] = c;
d[a][b] = c;
cnt[a][b] = 1;
}
floyd();
if(ans < 1e18) {
cout << ans << ' ' << res % mod << endl;
} else cout << -1 << ' ' << -1 << endl;
}
signed main() {
kanm7;
int T; cin >> T;
while(T --) {
solve();
}
return 0;
}