#include <bits/stdc++.h>

using namespace std;

#define int long long
#define all(v) (v).begin(), (v).end()
#define resz(v, ...) (v).clear(), (v).resize(__VA_ARGS__)
#define reps(i, m, n) for(int i = (int)(m); i < (int)(n); i++)
#define rep(i, n) reps(i, 0, n)

template<class T1, class T2> void chmin(T1 &a, T2 b){if(a>b)a=b;}
template<class T1, class T2> void chmax(T1 &a, T2 b){if(a<b)a=b;}

typedef pair<int, int> Pi;
typedef tuple<int, int, int> Ti;
typedef vector<int> vint;

const int inf = 1LL << 55;
const int mod = 1e9 + 7;

struct UnionFind {
  vector<int> data;
  UnionFind(){}
  void init(int n) {
    data.clear();
    data.resize(n, -1);
  }
  int find(int x) {
    return data[x] < 0 ? x : data[x] = find(data[x]);
  }
  int size(int x) {
    return -data[find(x)];
  }
  bool same(int x, int y) {
    return find(x) == find(y);
  }
  void unite(int x, int y) {
    x = find(x), y = find(y);
    if(x == y) return;
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y]; data[y] = x;
  }
};

struct edge {
  int u, v, w, id;
  edge(){}
  edge(int u, int v, int w, int id):u(u), v(v), w(w), id(id){}
  bool operator < (const edge& e) const {
    return w < e.w;
  }
};

int N, M;
vector<edge> E;
vector<int> used;
int weight;
UnionFind uf;

int Kruskal(int id = -1) {
  uf.init(N);
  int res = 0;
  for(auto e : E) {
    if(e.id == id) {
      weight = e.w;
      continue;
    }
    if(!uf.same(e.u, e.v)) {
      uf.unite(e.u, e.v);
      res += e.w;
      if(id == -1) used.push_back(e.id);
    }
  }
  if(uf.size(0) < N) return inf;
  return res;
}

signed main()
{
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);

  cin >> N >> M;
  rep(i, M) {
    int s, d, c;
    cin >> s >> d >> c;
    --s, --d;
    E.emplace_back(s, d, c, i);
  }
  sort(all(E));
  int mn = Kruskal();
  int ans = 0, cost = 0;
  for(int i : used) {
    if(Kruskal(i) > mn) ans++, cost += weight;
  }
  cout << ans << " " << cost << endl;

  return 0;
}