環的權值為邊權最小值,可以想到從大到小遍歷權值,如果一條邊加入後出現了環說明這條邊的邊權就是整個環的權值。
類似 Kruskal,我們把邊權從大到小排序,然後用並查集維護連通情況,算出最小的權值。然後跑 dfs 找環輸出方案。時間複雜度 \(\mathcal{O}(m\log{m} + n)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 7;
int read() {
char c = getchar();
int x = 0, p = 1;
while ((c < '0' || c > '9') && c != '-') c = getchar();
if (c == '-') p = -1, c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return x * p;
}
int n, m;
vector <int> to[N];
struct Edge {
int u, v, w;
bool operator < (const Edge p) const {
return w > p.w;
}
} t[N];
struct DSU {
int fa[N];
void init() {
for (int i = 1; i <= n; i ++) fa[i] = i;
}
int find(int x) {
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
void merge(int i, int j) {
int x = find(i), y = find(j);
fa[x] = y;
}
} D;
bool vis[N];
stack <int> st;
bool flg;
void dfs(int u, int p, int res) {
vis[u] = 1; st.push(u);
if (u == p) {
flg = 1; cout << res << ' ' << (int) st.size() << '\n';
while (!st.empty()) {
cout << st.top() << ' ';
st.pop();
}
cout << '\n';
return ;
}
for (int v : to[u]) {
if (!vis[v]) {
dfs(v, p, res);
if (flg) return ;
}
}
st.pop();
}
void solve() {
n = read(), m = read();
for (int i = 1; i <= n; i ++)
to[i].clear();
for (int i = 1; i <= m; i ++)
t[i].u = read(), t[i].v = read(), t[i].w = read();
sort(t + 1, t + m + 1); D.init();
long long ans = 2e18; flg = 0;
for (int i = 1; i <= m; i ++) {
int u = t[i].u, v = t[i].v, w = t[i].w;
if (D.find(u) != D.find(v)) {
D.merge(u, v);
} else {
ans = i;
}
}
fill(vis + 1, vis + n + 1, 0);
for (int i = 1; i < ans; i ++) {
to[t[i].u].push_back(t[i].v);
to[t[i].v].push_back(t[i].u);
}
dfs(t[ans].u, t[ans].v, t[ans].w);
}
signed main() {
int t = read();
while (t --) solve();
return 0;
}