
//#include <bits/stdc++.h>
#include <stdio.h>
#include <vector>
#include <iostream>
using namespace std;
// ================= 代码实现开始 =================
#define MAXN 100000+10
/* 请在这里定义你需要的全局变量 */
//struct Edge {int u, v,w};
//vector<Edge> E;
typedef struct{
int u, v, w;
}Edge;
Edge E[MAXN];
int k; //需要k个柿饼
int nEdge = 0; // nEdge表示加入的边数目
int par[MAXN],Rank[MAXN]; //并查集的父节点,以及秩
// 给定n个点m条边的有权无向图,求最小的k-生成森林的边权和
// k-生成森林:k-生成森林是原图的一个生成子图,并且使得其中存在k个森林
// n:如题意 n个点
// m:如题意 m条边
// k:如题意 能否有k个子树(柿子)
// E:大小为m的数组,表示m条情报
// 返回值:若能构成k-生成森林,则返回最小k-生成森林的边权和,否则返回-1
int cmp(const void*a, const void *b){
return ((Edge*)a)->w - ((Edge*)b)->w;
}
void Init(int n){
for(int i=0; i<n; i++)
{Rank[i] = 0;par[i] = i;}
}
int find(int x){
int root = x;
while(root != par[root]) root = par[root];
while(x != root){
int t = par[x];
par[x] = root;
x = t;
} // 将root值逐个赋予同一树内节点的parent值即路径压缩path compression
return root;
//return par[x]==x ? x : par[x]=find(par[x]);
}
void unite(int x, int y){
x = find(x);
y = find(y);
if(Rank[x]<Rank[y]){
par[x] = y;
}
else{
par[y] = x;
if( Rank[x] == Rank[y] ) Rank[x]++;
}
}
int kruskal(int m, int n){
int res = 0; // res表示需要的权重和
//将边按照权值从小到大进行排列
qsort(E, m, sizeof(E[0]), cmp);
/* for(int i =0 ;i<m;i++){
printf("%d%d%d", E[i].u , E[i].v, E[i].w);
} */
for(int i=0; i<m && n-nEdge != k ;i++){
//判断当前这条边的两个端点是否属于同一棵树
if(find(E[i].u) != find(E[i].v) ){
unite(E[i].u, E[i].v);
res += E[i].w;
nEdge++;
}
}
// if(nEdge<n-1) res = -1;
return res;
// 如果加入边的数量小于n-1 那么说明该无向图不连通,即不存在最小生成树
}
int getAnswer(int n, int m, int k)
{
int ans = kruskal(m,n);
if ((n-nEdge) > k)
ans = -1;
return ans;
}
// ================= 代码实现结束 =================
int main()
{
int n, m;
scanf("%d%d%d", &n, &m, &k);
Init(m);
for (int i = 0; i < m; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
E[i].u = u ; E[i].v = v ; E[i].w = w ;
}
cout << getAnswer(n, m, k) << endl;
return 0;
}