#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <numeric>
using namespace std;
// Struct đại diện cho một cạnh trong đồ thị
struct Edge {
int u, v, c; // u và v là 2 đỉnh của cạnh, c là chi phí
bool operator<(const Edge& other) const { // So sánh các cạnh theo chi phí
return c < other.c;
}
};
// Struct cho Union-Find (Disjoint Set Union)
struct UnionFind {
vector<int> parent, rank; // parent[i] là cha của đỉnh i, rank[i] là độ cao của cây tại đỉnh i
UnionFind(int n) {
parent.resize(n + 1); // Khởi tạo mảng cha
rank.resize(n + 1, 0); // Khởi tạo độ cao ban đầu là 0
for (int i = 1; i <= n; i++) parent[i] = i; // Mỗi đỉnh là một tập riêng ban đầu
}
// Tìm tập cha của đỉnh u
int find(int u) {
if (u != parent[u]) parent[u] = find(parent[u]); // Áp dụng đường đi nén để tối ưu
return parent[u];
}
// Hợp nhất hai tập chứa u và v
bool unionSet(int u, int v) {
u = find(u); // Tìm tập cha của u
v = find(v); // Tìm tập cha của v
if (u == v) return false; // Nếu u và v cùng tập, không thể hợp nhất
if (rank[u] < rank[v]) swap(u, v); // Đảm bảo tập có độ cao thấp hơn được gộp vào tập cao hơn
parent[v] = u; // Hợp nhất tập v vào tập u
if (rank[u] == rank[v]) rank[u]++; // Tăng độ cao nếu hai tập có cùng độ cao
return true;
}
};
// Tìm cây khung nhỏ nhất (MST) bằng thuật toán Kruskal
// Có thể bỏ qua một cạnh cụ thể `ignoreEdge` nếu được chỉ định
vector<Edge> findMST(int n, const vector<Edge>& edges, Edge ignoreEdge = {-1, -1, -1}) {
UnionFind uf(n); // Khởi tạo Union-Find
vector<Edge> mstEdges; // Lưu các cạnh của MST
int totalCost = 0; // Tổng chi phí của MST
for (const Edge& edge : edges) {
if (edge.u == ignoreEdge.u && edge.v == ignoreEdge.v && edge.c == ignoreEdge.c) continue; // Bỏ qua cạnh ignoreEdge
if (uf.unionSet(edge.u, edge.v)) { // Nếu hợp nhất được u và v (không tạo chu trình)
mstEdges.push_back(edge); // Thêm cạnh vào MST
totalCost += edge.c; // Cộng chi phí của cạnh vào tổng chi phí
}
}
if (mstEdges.size() == n - 1) return mstEdges; // Trả về MST nếu có đủ n-1 cạnh
return {}; // Trả về rỗng nếu không tìm được MST hợp lệ
}
int main() {
int n, m, k; // n: số đỉnh, m: số cạnh, k: số kế hoạch cần tìm
cin >> n >> m >> k;
vector<Edge> edges(m); // Danh sách các cạnh
for (int i = 0; i < m; i++) {
cin >> edges[i].u >> edges[i].v >> edges[i].c; // Nhập cạnh với u, v và chi phí c
}
sort(edges.begin(), edges.end()); // Sắp xếp các cạnh theo chi phí tăng dần (chuẩn bị cho Kruskal)
// Tìm cây khung nhỏ nhất ban đầu (MST đầu tiên)
vector<Edge> mst = findMST(n, edges);
if (mst.empty()) { // Nếu không tìm được MST hợp lệ
cout << -1 << endl; // In -1 và thoát
return 0;
}
// Priority queue để lưu các tổng chi phí của các MST, sắp xếp theo thứ tự tăng dần
priority_queue<int, vector<int>, greater<int>> costs;
// Tính tổng chi phí của MST ban đầu và đưa vào priority queue
costs.push(accumulate(mst.begin(), mst.end(), 0, [](int sum, const Edge& e) { return sum + e.c; }));
// Tìm các cây khung khác bằng cách loại bỏ lần lượt từng cạnh trong MST
for (const Edge& e : mst) {
vector<Edge> newMST = findMST(n, edges, e); // Tìm cây khung mới khi bỏ cạnh e
if (!newMST.empty()) { // Nếu tìm được cây khung mới hợp lệ
// Tính tổng chi phí của cây khung mới
int cost = accumulate(newMST.begin(), newMST.end(), 0, [](int sum, const Edge& e) { return sum + e.c; });
costs.push(cost); // Thêm tổng chi phí vào priority queue
}
}
// Lấy cây khung thứ k (nếu có)
for (int i = 1; i < k; i++) {
if (costs.empty()) { // Nếu không còn cây khung nào trong priority queue
cout << -1 << endl; // In -1 và thoát
return 0;
}
costs.pop(); // Bỏ cây khung nhỏ nhất hiện tại để tiến đến cây tiếp theo
}
// In tổng chi phí của cây khung thứ k (nếu có)
cout << (costs.empty() ? -1 : costs.top()) << endl;
return 0;
}