#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
#include <set>
#include <map>
using namespace std;

template<typename flow_t, typename cost_t>
struct PrimalDual {
    struct Edge {
        int to; flow_t cap; cost_t cost; int rev; bool isrev;
        Edge(int to, flow_t cap, cost_t cost, int rev, bool isrev) \
            : to(to), cap(cap), cost(cost), rev(rev), isrev(isrev) { }
    };
    const cost_t INF;
    vector<vector<Edge>> graph;
    vector<cost_t> potential, min_cost;
    vector<int> prevv, preve;
    PrimalDual(int V) : INF(numeric_limits<cost_t>::max()), graph(V) { }
    void add_edge(int from, int to, flow_t cap, cost_t cost) {
        graph[from].emplace_back(Edge(to, cap, cost, graph[to].size(), false));
        graph[to].emplace_back(Edge(from, 0, -cost, graph[from].size() - 1, true));
    }
    cost_t min_cost_flow(int s, int t, flow_t f) {
        int V = (int)graph.size();
        cost_t ret = 0;
        using Pi = pair<cost_t, int>;
        priority_queue<Pi, vector<Pi>, greater<Pi>> que;
        potential.assign(V, 0);
        preve.assign(V, -1); prevv.assign(V, -1);
        while (f > 0) {
            min_cost.assign(V, INF); min_cost[s] = 0;
            que.emplace(0, s);
            while (!que.empty()) {
                Pi p = que.top(); que.pop();
                if (min_cost[p.second] < p.first) continue;
                for (int i = 0; i < graph[p.second].size(); i++) {
                    Edge &e = graph[p.second][i];
                    cost_t nextCost = min_cost[p.second] + e.cost;
                    nextCost += potential[p.second] - potential[e.to];
                    if (e.cap > 0 && min_cost[e.to] > nextCost) {
                        min_cost[e.to] = nextCost;
                        prevv[e.to] = p.second, preve[e.to] = i;
                        que.emplace(min_cost[e.to], e.to);
                    }
                }
            }
            if (min_cost[t] == INF) return -1;  // 経路が見つからなかった
            // s-t 間最短路に沿って目一杯流す
            for (int v = 0; v < V; v++) potential[v] += min_cost[v];
            flow_t addflow = f;
            for (int v = t; v != s; v = prevv[v]) {
                addflow = min(addflow, graph[prevv[v]][preve[v]].cap);
            }
            f -= addflow;
            ret += addflow * potential[t];
            for (int v = t; v != s; v = prevv[v]) {
                Edge &e = graph[prevv[v]][preve[v]];
                e.cap -= addflow;
                graph[v][e.rev].cap += addflow;
            }
        }
        return ret;
    }
    friend ostream& operator<<(ostream& os, PrimalDual& mcf) {
        for (int i = 0; i < mcf.graph.size(); i++) {
            for (auto &e: mcf.graph[i]) if (!e.isrev) {
                auto &rev_e = mcf.graph[e.to][e.rev];
                os << i << " -> " << e.to << " (flow: " << rev_e.cap << " / ";
                os << e.cap + rev_e.cap << ", cost: " << e.cost << ")" << endl;
            }
        }
        return os;
    }
};

int main() {
    const int M = 20;
    int n;
    while (cin >> n, n) {   // 駅の数
        vector<int> m(n - 1);
        vector<vector<int>> x(n - 1), y(n - 1), c(n - 1);
        vector<set<int>> tArrs(n - 1);
        vector<map<int, int>> tArr2id(n - 1);

        for (int i = 0; i < n - 1; i++) {       // stations
            cin >> m[i];
            x[i].resize(m[i]); y[i].resize(m[i]); c[i].resize(m[i]);
            for (int j = 0; j < m[i]; j++) {    // trains
                // 出発時刻(xi,j), 到着時刻(yi,j), 運賃(ci,j)
                cin >> x[i][j] >> y[i][j] >> c[i][j];
                tArrs[i].insert(y[i][j]);
            }
            int id = 0;
            for (auto tArr: tArrs[i]) tArr2id[i][tArr] = id++;
        }
        int g; cin >> g;    // 修学旅行に参加するクラス数

        PrimalDual<int, int> mcf((n - 1) * M * 3 + 2);
		int source = (n - 1) * M * 3, sink = (n - 1) * M * 3 + 1;
        //    0           1 .. n-2                         n-1
        // S, departure, [arrival, buff, departure]_{n-2}, arrival, T
        auto departure = [=](int sta, int id) { return sta * M * 3 + id; };
        auto arrival = [=](int sta, int id) { return sta * M * 3 - M * 2 + id; };
        auto buff = [=](int sta, int id) { return sta * M * 3 - M + id; };
        for (int id = 0; id < M; id++) {
            // S -> departure
            if (id < m[0]) mcf.add_edge(source, departure(0, id), 1, 0);
            for (int sta = 1; sta < n - 1; sta++) {
                // arrival -> buff
                if (id >= m[sta - 1]) continue;
                mcf.add_edge(arrival(sta, id), buff(sta, id), 1, 0);
            }
            // arrival -> T
            if (id >= tArr2id[n - 2].size()) continue;
            mcf.add_edge(arrival(n - 1, id), sink, 1, 0);
        }
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < m[i]; j++) {
                // departure[i] -> arrival[i + 1]
                int id = tArr2id[i][y[i][j]];
                mcf.add_edge(departure(i, j), arrival(i + 1, id), 1, c[i][j]);
            }
        }
        for (int i = 1; i < n - 1; i++) {
            for (int j = 0; j < m[i - 1]; j++) for (int k = 0; k < m[i]; k++) {
                // buff[i] -> departure[i]
                int tArr = y[i - 1][j], tDep = x[i][k];
                if (tArr > tDep) continue;
                int id = tArr2id[i - 1][y[i - 1][j]];
                mcf.add_edge(buff(i, id), departure(i, k), 1, 0);
            }
        }
        int flow, cost = 0, ret;
        for (flow = 1; flow <= g; flow++) {
            if ((ret = mcf.min_cost_flow(source, sink, 1)) == -1) break;
            cost += ret;
        }
        flow--;
        cout << flow << " " << cost << endl;
    }
    return 0;
}


Measured memory_bytes: 5968