#include <algorithm>
#include <cmath>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <queue>
#include <set>
#include <tuple>
#include <vector>

using namespace std;

#define rep(i, n) for (int64_t i = 0; i < (n); i++)
#define irep(i, n) for (int64_t i = 0; i <= (n); i++)
#define rrep(i, n) for (int64_t i = (n)-1; i >= 0; i--)
#define rirep(i, n) for (int64_t i = n; i >= 0; i--)

class WeightedFlowNetwork {
  struct Edge {
    int from;
    int to;
    int64_t capacity;
    int64_t flow;
    int64_t cost;
  };

  vector<vector<int>> outEdge, inEdge;
  vector<Edge> edgeData;
  vector<int> potential;

 public:
  WeightedFlowNetwork(const int size)
      : outEdge(size), inEdge(size), potential(size, 0) {}

  void addEdge(const int from, const int to, const int64_t capacity,
               const int64_t cost) {
    const int eId = edgeData.size();
    Edge eData;
    eData.from = from;
    eData.to = to;
    eData.flow = 0;
    eData.capacity = capacity;
    eData.cost = cost;
    edgeData.push_back(eData);
    outEdge[from].push_back(eId);
    inEdge[to].push_back(eId);
  }

  /// returns (|flow|, mincost)
  pair<int64_t, int64_t> minCostFlow(const int source, const int sink,
                                     const int64_t f) {
    const int n = outEdge.size();
    int64_t demand = f;

    while (true) {
      // check if reachable to sink
      const int64_t INF = 1'000'000'000;
      using P = pair<int64_t, int>;
      vector<int> parent(n, -1);
      vector<int64_t> dist(n, INF);
      priority_queue<P, vector<P>, greater<P>> q;

      dist[source] = 0;
      q.push(P(0, source));

      while (!q.empty()) {
        int64_t d;
        int v;
        tie(d, v) = q.top();
        q.pop();

        if (d > dist[v]) {
          continue;
        }

        for (const int e : outEdge[v]) {
          const Edge &data = edgeData[e];
          const int nv = data.to;
          const int64_t nd = d + data.cost + potential[v] - potential[nv];
          if (dist[nv] > nd && data.capacity - data.flow > 0) {
            q.push(P(nd, nv));
            dist[nv] = nd;
            parent[nv] = e;
          }
        }

        for (const int e : inEdge[v]) {
          const Edge &data = edgeData[e];
          const int nv = data.from;
          const int64_t nd = d - data.cost + potential[v] - potential[nv];
          if (dist[nv] > nd && data.flow > 0) {
            q.push(P(nd, nv));
            dist[nv] = nd;
            parent[nv] = e;
          }
        }
      }

      for (int i = 0; i < potential.size(); i++) potential[i] += dist[i];

      if (parent[sink] < 0 || demand == 0) {
        break;
      }

      // select path from source to sink
      int64_t fDiff = demand;
      int v = sink;
      while (v != source) {
        const int e = parent[v];
        const Edge &data = edgeData[e];
        if (data.to == v) {
          fDiff = min(fDiff, data.capacity - data.flow);
          v = data.from;
        } else {
          fDiff = min(fDiff, data.flow);
          v = data.to;
        }
      }

      // update flow on the path
      v = sink;
      while (v != source) {
        const int e = parent[v];
        Edge &data = edgeData[e];
        if (data.to == v) {
          data.flow += fDiff;
          v = data.from;
        } else {
          data.flow -= fDiff;
          v = data.to;
        }
      }

      demand -= fDiff;
    }

    int64_t rCost = 0;
    for (const Edge &eData : edgeData) {
      rCost += eData.cost * eData.flow;
    }
    return pair<int64_t, int64_t>(f - demand, rCost);
  }
};

int main() {
  int n;
  cin >> n;

  while (n != 0) {
    using T = tuple<int, int, int>;
    vector<vector<T>> train(n - 1);
    rep(i, n - 1) {
      int m;
      cin >> m;
      rep(j, m) {
        int x, y, c;
        cin >> x >> y >> c;
        train[i].push_back(T(x, y, c));
      }
    }
    int g;
    cin >> g;

    const int INF = numeric_limits<int>::max();
    vector<vector<int>> departure(n), arrival(n);
    rep(i, n - 1) {
      rep(j, train[i].size()) {
        int x, y;
        tie(x, y, ignore) = train[i][j];

        departure[i].push_back(x);
        arrival[i + 1].push_back(y);
      }
    }
    departure[0].push_back(0);
    departure[n - 1].push_back(INF);

    rep(i, n) {
      sort(departure[i].begin(), departure[i].end());
      departure[i].erase(unique(departure[i].begin(), departure[i].end()),
                         departure[i].end());
      sort(arrival[i].begin(), arrival[i].end());
      arrival[i].erase(unique(arrival[i].begin(), arrival[i].end()),
                       arrival[i].end());
    }

    int nodeNum = 0;
    vector<vector<int>> depId(n), arrId(n);
    rep(i, n) {
      rep(j, departure[i].size()) { depId[i].push_back(nodeNum++); }
      rep(j, arrival[i].size()) { arrId[i].push_back(nodeNum++); }
    }

    const int source = depId[0][0];
    const int sink = depId[n - 1].back();

    WeightedFlowNetwork net(nodeNum);
    rep(i, n) {
      rep(j, departure[i].size() - 1) {
        net.addEdge(depId[i][j], depId[i][j + 1], g, 0);
      }
    }

    rep(i, n - 1) {
      for (auto t : train[i]) {
        int x, y, c;
        tie(x, y, c) = t;
        const int fromOffset =
            lower_bound(departure[i].begin(), departure[i].end(), x) -
            departure[i].begin();
        const int toOffset =
            lower_bound(arrival[i + 1].begin(), arrival[i + 1].end(), y) -
            arrival[i + 1].begin();
        const int fromId = depId[i][fromOffset];
        const int toId = arrId[i + 1][toOffset];

        net.addEdge(fromId, toId, 1, c);
      }
    }

    rep(i, n) {
      rep(j, arrival[i].size()) {
        const int fromOffset = j;
        const int toOffset = lower_bound(departure[i].begin(),
                                         departure[i].end(), arrival[i][j]) -
                             departure[i].begin();
        if (toOffset >= departure[i].size()) {
          continue;
        }

        const int fromId = arrId[i][fromOffset];
        const int toId = depId[i][toOffset];

        net.addEdge(fromId, toId, 1, 0);
      }
    }

    int flow, cost;
    tie(flow, cost) = net.minCostFlow(source, sink, g);
    cout << flow << " " << cost << endl;

    cin >> n;
  }
  return 0;
}


Measured memory_bytes: 3512