#include <bits/stdc++.h>
using namespace std;
template <typename T>
struct edge {
  int src, to;
  T cost;
  edge(int to, T cost) : src(-1), to(to), cost(cost) {}
  edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}
  edge &operator=(const int &x) {
    to = x;
    return *this;
  }
  operator int() const { return to; }
};
template <typename T>
using Edges = vector<edge<T>>;
template <typename T>
using WeightedGraph = vector<Edges<T>>;
using UnWeightedGraph = vector<vector<int>>;
template <typename T>
using Matrix = vector<vector<T>>;
template <typename G>
struct CentroidDecomposition {
  const G &g;
  vector<int> sub;
  vector<vector<int>> belong;
  vector<bool> v;
  CentroidDecomposition(const G &g)
      : g(g), sub(g.size()), v(g.size()), belong(g.size()) {}
  inline int build_dfs(int idx, int par) {
    sub[idx] = 1;
    for (auto &to : g[idx]) {
      if (to == par || v[to]) continue;
      sub[idx] += build_dfs(to, idx);
    }
    return sub[idx];
  }
  inline int search_centroid(int idx, int par, const int mid) {
    for (auto &to : g[idx]) {
      if (to == par || v[to]) continue;
      if (sub[to] > mid) return search_centroid(to, idx, mid);
    }
    return idx;
  }
  inline void belong_dfs(int idx, int par, int centroid) {
    belong[idx].emplace_back(centroid);
    for (auto &to : g[idx]) {
      if (to == par || v[to]) continue;
      belong_dfs(to, idx, centroid);
    }
  }
  inline int build(UnWeightedGraph &t, int idx) {
    int centroid = search_centroid(idx, -1, build_dfs(idx, -1) / 2);
    v[centroid] = true;
    belong_dfs(centroid, -1, centroid);
    for (auto &to : g[centroid]) {
      if (!v[to]) t[centroid].emplace_back(build(t, to));
    }
    v[centroid] = false;
    return centroid;
  }
  inline int build(UnWeightedGraph &t) {
    t.resize(g.size());
    return build(t, 0);
  }
};
template <typename T>
vector<T> dijkstra(WeightedGraph<T> &g, int s) {
  const auto INF = numeric_limits<T>::max();
  vector<T> dist(g.size(), INF);
  using Pi = pair<T, int>;
  priority_queue<Pi, vector<Pi>, greater<Pi>> que;
  dist[s] = 0;
  que.emplace(dist[s], s);
  while (!que.empty()) {
    T cost;
    int idx;
    tie(cost, idx) = que.top();
    que.pop();
    if (dist[idx] < cost) continue;
    for (auto &e : g[idx]) {
      auto next_cost = cost + e.cost;
      if (dist[e.to] <= next_cost) continue;
      dist[e.to] = next_cost;
      que.emplace(dist[e.to], e.to);
    }
  }
  return dist;
}
pair<long long, long long> dfs(int v, int p, WeightedGraph<long long> &g) {
  long long sz = 1;
  long long val = 0;
  for (auto i : g[v]) {
    if (i.to == p) continue;
    auto tmp = dfs(i.to, v, g);
    if (tmp.first % 2 == 0)
      val += tmp.second;
    else {
      sz += tmp.first;
      val += tmp.second + i.cost;
    }
  }
  return make_pair(sz, val);
}
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  int t;
  cin >> t;
  vector<pair<long long, long long>> ans;
  for (int i = 0; i < t; i++) {
    int k;
    cin >> k;
    UnWeightedGraph g(2 * k);
    WeightedGraph<long long> gw(2 * k);
    for (int j = 0; j < 2 * k - 1; j++) {
      int x, y, c;
      cin >> x >> y >> c;
      --x;
      --y;
      g[x].emplace_back(y);
      g[y].emplace_back(x);
      gw[x].emplace_back(y, c);
      gw[y].emplace_back(x, c);
    }
    CentroidDecomposition<UnWeightedGraph> cd(g);
    UnWeightedGraph tree;
    int root = cd.build(tree);
    auto vec = dijkstra(gw, root);
    long long B = 0;
    for (int i = 0; i < vec.size(); i++) {
      B += vec[i];
    }
    long long G = 0;
    G = dfs(0, -1, gw).second;
    ans.push_back(make_pair(G, B));
  }
  for (int i = 0; i < t; i++) {
    cout << ans[i].first << " " << ans[i].second << endl;
  }
  return 0;
}
