Problem:
Problem E: School Excursion
春、それは修学旅行の時期である。
会津大学附属小学校（会津大小）でも、来年度の修学旅行の計画が建てられていた。
修学旅行は電車で移動することが伝統になっている。
会津若松市内では電車を利用する機会が少ないためだ。
しかし、学校に届いたある苦情により教員たちは頭を悩ませていた。
それは、「ある駅で大人数で乗り換えを行っていたので、その駅を利用する他の乗客に迷惑であった」という内容だった。
とくに、電車を降りたあとホームからの通路で大混雑してしまったという。
それは近年、会津大小の生徒数が急上昇しているからであった。
その原因は日本において競技プログラミングが大流行し、
小学校から競技プログラマーを養成するための訓練を行っている会津大小に生徒が集まったからである。
会津大小競技プログラミング部長であるあなたは、修学旅行を楽しみにしている。
修学旅行を成功させたいあなたは教員たちに提案をした。
「クラスごとに行動して、同じ駅に同時に到着しないようにしましょう！！　競技プログラミングの知識を応用すれば簡単です！！」
複数のクラスが同時に同じ駅に到着することがないように移動するとき（同じ駅に複数のクラスが滞在することは可能）、
出発する駅から目的地まで到達できる最大のクラス数を求め、そのときの最小の運賃を求めるプログラムをつくりなさい。
ただし、ある駅で同時に到着する電車と発車する電車が存在する場合、乗り継ぎが可能である。つまり、乗り継ぎに要する時間は無視して良い。
Input
入力は複数のデータセットから構成される。
データセットの数は20個以下である。
各データセットの形式は次に示す通りである。
n
m
1
x
1,1
y
1,1
c
1,1
x
1,2
y
1,2
c
1,2
...
x
1,m1
y
1,m1
c
1,m1
m
2
x
2,1
y
2,1
c
2,1
x
2,2
y
2,2
c
2,2
...
x
2,m2
y
2,m2
c
2,m2
...
m
n-1
x
n-1,1
y
n-1,1
c
n-1,1
x
n-1,2
y
n-1,2
c
n-1,2
...
x
n-1,m2
y
n-1,m2
c
n-1,m2
g
n( 2 ≤ n ≤ 100)は駅の数を表す。
駅の数は出発する駅と目的地の駅、乗り換えする駅を含む。
駅は出発する駅を1番目、最初に乗り換える駅を2番目の駅と数え、目的地をn番目の駅とする。
m
i
はi番目の駅からi+1番目の駅への電車の数を表す。
m
i
(1 ≤ m ≤ 20)個の電車の情報が続く。
x
i,j
y
i,j
c
i,j
はi番目の駅からi+1番目の駅に向かう電車の出発時刻(x
i,j
)到着時刻(y
i,j
)運賃(c
i,j
)を表す。
(0 ≤ x
i,j
, y
i,j
≤ 200000 かつ x
i,j
≤ y
i,j
)
(1 ≤ c
i,j
≤ 1000)

その後g(1≤ g ≤ 10)が続く。
gは修学旅行に参加するクラス数を表す。
与えられる数（n,m,x,y,c,g)はすべて整数である。
入力の終了はひとつの0を含む１行で示される。
Output
一行に到着可能なクラス数とそのときの最小運賃をこの順に出力せよ。
もし、ただ一つのクラスも到着できない場合は0 0を出力せよ。
Sample Input
2
4
1 2 10
2 2 5
1 3 20
2 3 10
10
3
2
1 2 5
0 3 4
3
10 11 100
2 12 2
12 12 3
10
0
Sample Output
2 15
2 111

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using pll = pair<ll, ll>;
#define vc vector
using vl = vc<ll>;

#define ov(a, b, c, name, ...) name
#define rep2(i, l, r) for(ll i = (l); i < ll(r); i++)
#define rep1(i, n) rep2(i, 0, n)
#define rep(...) ov(__VA_ARGS__, rep2, rep1)(__VA_ARGS__)
#define fec(...) for(const auto& __VA_ARGS__)
#define per(i, a, b) for(ll i = ll(a) - 1; i >= ll(b); i--)

#define ALL(x) begin(x), end(x)
#define SZ(x) (ll) size(x)
#define LB(v, x) (lower_bound(ALL(v), x) - begin(v))
#define eb emplace_back

bool chmin(auto& a, auto b) { return a > b ? a = b, 1 : 0; }
bool chmax(auto& a, auto b) { return a < b ? a = b, 1 : 0; }
const ll INF = ll(4e18) + 100;

void printvec(const auto& v) {
   for(auto it = begin(v); it != end(v); it++) cout << *it << " ";
   cout << endl;
}

#ifdef LOCAL
#define local(...) __VA_ARGS__
#else
#define local(...)
#endif

struct MCF {
   struct edge {
      int to;
      ll cap, cost;
      int rev;
      bool isrev;
   };
   vc<vc<edge>> g;
   vl pot, cost;
   vc<int> pv, pe;
   MCF(int n) : g(n) {}
   void add(int u, int v, ll cap, ll cost) {
      g[u].eb(v, cap, cost, SZ(g[v]), false);
      g[v].eb(u, 0, -cost, SZ(g[u]) - 1, true);
   }
   // (flow, cost)  f は流量制限
   pll flow(int s, int t, ll f) {
      int n = g.size();
      ll F = 0, C = 0;
      using P = pair<ll, int>;
      priority_queue<P, vc<P>, greater<P>> pq;
      pot.assign(n, 0), pe.assign(n, -1), pv.assign(n, -1);
      while(f) {
         cost.assign(n, INF);
         pq.emplace(0, s);
         cost[s] = 0;
         while(!pq.empty()) {
            auto [c, x] = pq.top();
            pq.pop();
            if(cost[x] < c) continue;
            rep(i, g[x].size()) {
               edge& e = g[x][i];
               ll ncost = cost[x] + e.cost + pot[x] - pot[e.to];
               if(e.cap and chmin(cost[e.to], ncost)) {
                  pv[e.to] = x, pe[e.to] = i;
                  pq.emplace(cost[e.to], e.to);
               }
            }
         }
         if(cost[t] == INF) return {F, C};
         rep(i, n) pot[i] += cost[i];
         ll df = f;
         for(int v = t; v != s; v = pv[v]) chmin(df, g[pv[v]][pe[v]].cap);
         f -= df, F += df, C += df * pot[t];
         for(int v = t; v != s; v = pv[v]) {
            auto& e = g[pv[v]][pe[v]];
            e.cap -= df, g[v][e.rev].cap += df;
         }
      }
      return {F, C};
   }
};

int main() {
#ifndef LOCAL
   cin.tie(0)->sync_with_stdio(0);
#endif

   while(true) {
      ll N;
      cin >> N;
      if(N == 0) break;
      vc<array<ll, 6>> E;  // from_i, from_t, to_i, to_t, cap, cost
      vc<pll> V;
      rep(i, N - 1) {
         ll M;
         cin >> M;
         rep(j, M) {
            ll x, y, c;
            cin >> x >> y >> c;
            E.push_back({i, x, i + 1, y, 1, c});
            V.eb(i, x), V.eb(i + 1, y);
         }
      }
      ll g;
      cin >> g;
      sort(ALL(V)), V.erase(unique(ALL(V)), V.end());
      MCF G(2 * SZ(V) + 2);
      ll S = 2 * SZ(V), T = 2 * SZ(V) + 1;
      fec([ fi, ft, ti, tt, cap, cost ] : E) {
         ll u = LB(V, (pll{fi, ft}));
         ll v = LB(V, (pll{ti, tt}));
         G.add(2 * u + 1, 2 * v, cap, cost);
      }
      rep(u, SZ(V) - 1) {
         auto [i1, t1] = V.at(u);
         auto [i2, t2] = V.at(u + 1);
         if(i1 == i2) {
           G.add(2 * u + 1, 2 * (u + 1) + 1, g, 0);
         }
      }
      rep(u, SZ(V)) {
        auto [i, t] = V.at(u);
        G.add(2 * u, 2 * u + 1, 1, 0);
         if(i == 0) { G.add(S, 2 * u + 1, g, 0); }
         if(i == N - 1) { G.add(2 * u + 1, T, g, 0); }
      }
      auto [flow, cost] = G.flow(S, T, g);
      cout << flow << " " << cost << "\n";
   }
}
accuracy: 1
time_ms: 40
memory_kb: 4480
final_rank: 2

code_snippet: // #pragma GCC target("avx2")
// #pragma GCC optimize("O3")
// #pragma GCC optimize("unroll-loops")

#define CONSTANTS
#pragma GCC diagnostic ignored "-Wunused-parameter"
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;

constexpr long long INF = 1e18;
// constexpr long long INF = LONG_LONG_MAX;
// constexpr int MOD = 1000000007;
constexpr int MOD = 998244353;
constexpr long double EPS = 1e-10;
constexpr long double PI = M_PI;

using ll = long long; using ull = unsigned long long; using ld = long double; using pll = pair<ll, ll>; using pii = pair<int, int>; using pli = pair<ll, int>; using pil = pair<int, ll>; template<typename T> using vv = vector<vector<T>>; using vvl = vv<ll>; using vvi = vv<int>; using vvpll = vv<pll>; using vvpli = vv<pli>; using vvpil = vv<pil>;
#define name4(i, a, b, c, d, e, ...) e
#define rep(...) name4(__VA_ARGS__, rep4, rep3, rep2, rep1)(__VA_ARGS__)
#define rep1(i, a) for (ll i = 0, _aa = a; i < _aa; i++)
#define rep2(i, a, b) for (ll i = a, _bb = b; i < _bb; i++)
#define rep3(i, a, b, c) for (ll i = a, _bb = b; (c > 0 && a <= i && i < _bb) or (c < 0 && a >= i && i > _bb); i += c)
#define rrep(i, a, b) for (ll i=(a); i>(b); i--)
#define pb push_back
#define eb emplace_back
#define mkp make_pair
#define ALL(A) begin(A), end(A)
#define UNIQUE(A) sort(ALL(A)), A.erase(unique(ALL(A)), A.end())
#define elif else if
#define tostr to_string
#ifndef CONSTANTS
constexpr ll INF = 1e18; constexpr int MOD = 1000000007; constexpr ld EPS = 1e-10; constexpr ld PI = M_PI;
#endif
template<typename T1, typename T2> istream &operator>>(istream &is, pair<T1, T2> &p) { is >> p.first >> p.second; return is; } template<typename T> istream &operator>>(istream &is, vector<T> &v) { for (T &in : v) is >> in; return is; } template<typename T = ll> vector<T> LIST(int N) { vector<T> A(N); rep(i, N) { cin >> A[i]; } return A; }
template<typename T> bool chmax(T &x, T y) { return (y > x) ? x = y, true : false; }
template<typename T1, typename T2> ostream &operator<<(ostream &os, const pair<T1, T2> &p) { return os << p.first << ' ' << p.second; } template<class T, size_t... Is> void print_tuple(ostream &os, const T &arg, index_sequence<Is...>) { static_cast<void>(((os << (Is == 0 ? "" : " "), os << get<Is>(arg)), ...)); } template<class... Ts> ostream &operator<<(ostream &os, const tuple<Ts...> &arg) { print_tuple(os, arg, make_index_sequence<sizeof...(Ts)>()); return os; } template<typename T, size_t N> ostream &operator<<(ostream &os, const array<T, N> &arr) { rep(i, N) { os << arr[i]; if (i != (int)N - 1) { os << ' '; } } return os; } template<typename T, size_t N> void print(const array<T, N> &arr, string sep = " ") { rep(i, N) { cout << arr[i]; if (i != (int)N - 1) cout << sep; } cout << '\n'; } template<typename T> ostream &operator<<(ostream &os, const vector<T> &vec) { rep(i, vec.size()) { os << vec[i]; if (i != (int)vec.size() - 1) { os << ' '; } } return os; } template<typename T> void print(const vector<T> &vec, string sep = " ") { rep(i, vec.size()) { cout << vec[i]; if (i != (int)vec.size() - 1) cout << sep; } cout << '\n'; } template<typename T> void print(const deque<T> &que, string sep = " ") { vector<T> vec(ALL(que)); print(vec, sep); } template<typename T> void print(const set<T> &se, string sep = " ") { vector<T> vec(ALL(se)); print(vec, sep); } template<typename T> void print(const initializer_list<T> &li, string sep = " ") { vector<T> V(ALL(li)); print(V, sep); } void print() { cout << '\n'; } template<typename T> void print(T out) { cout << out << '\n'; }
#define debug(...) multi_debug(#__VA_ARGS__, __VA_ARGS__)
template<typename T> void multi_debug(string name, const vv<T> &grid) { cerr << name << ":" << endl; int H = grid.size(); int W = grid[0].size(); vector<int> mxlen(W); rep(h, H) { rep(w, W) { chmax(mxlen[w], (int)tostr(grid[h][w]).size()); } } rep(h, H) { rep(w, W) { int pad = mxlen[w] - (int)tostr(grid[h][w]).size(); cerr << string(pad, ' ') << grid[h][w]; if (w == W - 1) { cerr << endl; } else { cerr << ' '; } } } } template<class Tp, class... Ts> void multi_debug(string names, Tp arg, Ts... args) { if constexpr (sizeof...(Ts) == 0) { while (names.back() == ' ') { names.pop_back(); } cerr << names << ": " << arg << endl; } else { int n = names.size(), comma_pos = -1, paren_depth = 0, inside_quote = false; rep(i, n) { if (not inside_quote and paren_depth == 0 and names[i] == ',') { comma_pos = i; break; } if (names[i] == '\"' or names[i] == '\'') { if (i > 0 and names[i - 1] == '\\') continue; inside_quote ^= true; } if (not inside_quote and names[i] == '(') { paren_depth++; } if (not inside_quote and names[i] == ')') { paren_depth--; } } assert(comma_pos != -1); string name = names.substr(0, comma_pos); while (name.back() == ' ') { name.pop_back(); } cerr << name << ": " << arg << endl; int next_begin_at = comma_pos + 1; while (names[next_begin_at] == ' ') { next_begin_at++; } names = names.substr(next_begin_at); multi_debug(names, args...); } } template<typename T> void print(const vector<T> &V, char sep) { print(V, string{sep}); } template<typename T, size_t N> void print(const array<T, N> &arr, char sep) { print(arr, string{sep}); }

template<typename T> int bisect_left(const vector<T> &A, T val, int lo = 0) { return lower_bound(A.begin() + lo, A.end(), val) - A.begin(); } template<typename T> int bisect_right(const vector<T> &A, T val, int lo = 0) { return upper_bound(A.begin() + lo, A.end(), val) - A.begin(); }
template<typename T> struct Compress { int N; vector<T> dat; bool built = false; Compress() {} Compress(const vector<T> &A) : dat(A) { build(); } void build() { sort(dat.begin(), dat.end()); dat.erase(unique(dat.begin(), dat.end()), dat.end()); N = dat.size(); built = true; } void add(T x) { assert(not built); dat.emplace_back(x); } template<typename... Ts> void add(const T val, Ts... ts) { dat.emplace_back(val); if constexpr (sizeof...(Ts) > 0) { add(ts...); } } void add(const vector<T> &A) { for (auto a : A) { add(a); } } int zip(T x) { assert(built); return bisect_left(dat, x); } T unzip(int x) { assert(built); return dat[x]; } int operator[](T x) { return zip(x); } int size() { assert(built); return dat.size(); } T size(int i) { assert(built and 0 <= i and i < N); if (i == N - 1) { return 1; } else { return unzip(i + 1) - unzip(i); } } vector<T> zip(const vector<T> &A) { int M = A.size(); vector<T> res(M); for (int i = 0; i < M; i++) { res[i] = zip(A[i]); } return res; } };
namespace atcoder { namespace internal { template <class E> struct csr { std::vector<int> start; std::vector<E> elist; explicit csr(int n, const std::vector<std::pair<int, E>>& edges) : start(n + 1), elist(edges.size()) { for (auto e : edges) { start[e.first + 1]++; } for (int i = 1; i <= n; i++) { start[i] += start[i - 1]; } auto counter = start; for (auto e : edges) { elist[counter[e.first]++] = e.second; } } }; } }
namespace atcoder { namespace internal { template <class T> struct simple_queue { std::vector<T> payload; int pos = 0; void reserve(int n) { payload.reserve(n); } int size() const { return int(payload.size()) - pos; } bool empty() const { return pos == int(payload.size()); } void push(const T& t) { payload.push_back(t); } T& front() { return payload[pos]; } void clear() { payload.clear(); pos = 0; } void pop() { pos++; } }; } }
namespace atcoder { template <class Cap, class Cost> struct mcf_graph { public: mcf_graph() {} explicit mcf_graph(int n) : _n(n) {} int add_edge(int from, int to, Cap cap, Cost cost) { assert(0 <= from && from < _n); assert(0 <= to && to < _n); assert(0 <= cap); assert(0 <= cost); int m = int(_edges.size()); _edges.push_back({from, to, cap, 0, cost}); return m; } struct edge { int from, to; Cap cap, flow; Cost cost; }; edge get_edge(int i) { int m = int(_edges.size()); assert(0 <= i && i < m); return _edges[i]; } std::vector<edge> edges() { return _edges; } std::pair<Cap, Cost> flow(int s, int t) { return flow(s, t, std::numeric_limits<Cap>::max()); } std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) { return slope(s, t, flow_limit).back(); } std::vector<std::pair<Cap, Cost>> slope(int s, int t) { return slope(s, t, std::numeric_limits<Cap>::max()); } std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) { assert(0 <= s && s < _n); assert(0 <= t && t < _n); assert(s != t); int m = int(_edges.size()); std::vector<int> edge_idx(m); auto g = [&]() { std::vector<int> degree(_n), redge_idx(m); std::vector<std::pair<int, _edge>> elist; elist.reserve(2 * m); for (int i = 0; i < m; i++) { auto e = _edges[i]; edge_idx[i] = degree[e.from]++; redge_idx[i] = degree[e.to]++; elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}}); elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}}); } auto _g = internal::csr<_edge>(_n, elist); for (int i = 0; i < m; i++) { auto e = _edges[i]; edge_idx[i] += _g.start[e.from]; redge_idx[i] += _g.start[e.to]; _g.elist[edge_idx[i]].rev = redge_idx[i]; _g.elist[redge_idx[i]].rev = edge_idx[i]; } return _g; }(); auto result = slope(g, s, t, flow_limit); for (int i = 0; i < m; i++) { auto e = g.elist[edge_idx[i]]; _edges[i].flow = _edges[i].cap - e.cap; } return result; } private: int _n; std::vector<edge> _edges; struct _edge { int to, rev; Cap cap; Cost cost; }; std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g, int s, int t, Cap flow_limit) { std::vector<std::pair<Cost, Cost>> dual_dist(_n); std::vector<int> prev_e(_n); std::vector<bool> vis(_n); struct Q { Cost key; int to; bool operator<(Q r) const { return key > r.key; } }; std::vector<int> que_min; std::vector<Q> que; auto dual_ref = [&]() { for (int i = 0; i < _n; i++) { dual_dist[i].second = std::numeric_limits<Cost>::max(); } std::fill(vis.begin(), vis.end(), false); que_min.clear(); que.clear(); size_t heap_r = 0; dual_dist[s].second = 0; que_min.push_back(s); while (!que_min.empty() || !que.empty()) { int v; if (!que_min.empty()) { v = que_min.back(); que_min.pop_back(); } else { while (heap_r < que.size()) { heap_r++; std::push_heap(que.begin(), que.begin() + heap_r); } v = que.front().to; std::pop_heap(que.begin(), que.end()); que.pop_back(); heap_r--; } if (vis[v]) continue; vis[v] = true; if (v == t) break; Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second; for (int i = g.start[v]; i < g.start[v + 1]; i++) { auto e = g.elist[i]; if (!e.cap) continue; Cost cost = e.cost - dual_dist[e.to].first + dual_v; if (dual_dist[e.to].second - dist_v > cost) { Cost dist_to = dist_v + cost; dual_dist[e.to].second = dist_to; prev_e[e.to] = e.rev; if (dist_to == dist_v) { que_min.push_back(e.to); } else { que.push_back(Q{dist_to, e.to}); } } } } if (!vis[t]) { return false; } for (int v = 0; v < _n; v++) { if (!vis[v]) continue; dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second; } return true; }; Cap flow = 0; Cost cost = 0, prev_cost_per_flow = -1; std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}}; while (flow < flow_limit) { if (!dual_ref()) break; Cap c = flow_limit - flow; for (int v = t; v != s; v = g.elist[prev_e[v]].to) { c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap); } for (int v = t; v != s; v = g.elist[prev_e[v]].to) { auto& e = g.elist[prev_e[v]]; e.cap += c; g.elist[e.rev].cap -= c; } Cost d = -dual_dist[s].first; flow += c; cost += c * d; if (prev_cost_per_flow == d) { result.pop_back(); } result.push_back({flow, cost}); prev_cost_per_flow = d; } return result; } }; }
template<typename T> bool chmin(T &x, T y) { return (y < x) ? x = y, true : false; }
template<typename T> vector<T> bellman_ford(int N, const vector<tuple<int, int, T>> &edges, int src) { vector<T> res(N, INF); res[src] = 0; rep(_, N - 1) { for (auto [u, v, cost] : edges) { if (res[u] == INF) { continue; } chmin(res[v], res[u] + cost); } } auto tmp = res; rep(_, N - 1) { for (auto [u, v, cost] : edges) { if (res[u] == INF) { continue; } if (res[v] > res[u] + cost) { res[v] = -INF; } } } rep(i, N) { if (tmp[i] != res[i]) { res[i] = -INF; } } return res; }
template<typename Cap, typename Cost> struct MinCostFlow : atcoder::mcf_graph<Cap, Cost> { using base_type = atcoder::mcf_graph<Cap, Cost>; int N; bool has_negative = false; vector<tuple<int, int, Cost>> temp_edges; vector<Cap> caps; vector<Cost> potential; MinCostFlow(int N) : base_type(N), N(N), has_negative(false) { potential.assign(N, 0); } int add_edge(int from, int to, Cap cap, Cost cost) { if (cost < 0) { has_negative = true; } int m = temp_edges.size(); temp_edges.emplace_back(from, to, cost); caps.emplace_back(cap); return m; } void build(int s) { if (has_negative) { potential = bellman_ford(N, temp_edges, s); for (int i = 0; i < N; i++) { if (potential[i] >= INF) { potential[i] = 0; } assert(potential[i] > -INF); } } for (int i = 0; i < temp_edges.size(); i++) { auto [from, to, cost] = temp_edges[i]; base_type::add_edge(from, to, caps[i], cost + potential[from] - potential[to]); } } pair<Cap, Cost> flow(int s, int t) { return flow(s, t, numeric_limits<Cap>::max()); } pair<Cap, Cost> flow(int s, int t, Cap flow_limit) { return slope(s, t, flow_limit).back(); } vector<pair<Cap, Cost>> slope(int s, int t) { return slope(s, t, numeric_limits<Cap>::max()); } vector<pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) { build(s); auto result = base_type::slope(s, t, flow_limit); if (has_negative) { for (auto &[flow, cost] : result) { cost += (potential[t] - potential[s]) * flow; } } return result; } };
ll gridtoid(ll i, ll j, ll W) { return i * W + j; }

void solve() {
    ll N;
    cin >> N;
    if (N == 0) exit(0);
    Compress<ll> cp;
    vv<array<ll, 3>> XYC(N);
    rep(i, N - 1) {
        ll m;
        cin >> m;
        rep(_, m) {
            ll x, y, c;
            cin >> x >> y >> c;
            XYC[i].pb({x, y, c});
            cp.add(x, y);
        }
    }
    cp.build();
    ll cpsz = cp.size();
    ll g;
    cin >> g;

    // 頂点はV[駅][経過時間]
    ll L = N * cpsz;
    // 始点は頂点(0, 0)のout
    ll s = L;
    // 終点は頂点(N - 1, cpsz - 1)のout
    ll t = L * 2 - 1;
    MinCostFlow<ll, ll> mcf(L * 2);
    // in -> out
    rep(i, L) {
        // 流量1にして同時到着を防ぐ
        mcf.add_edge(i, L + i, 1, 0);
    }
    rep(i, N - 1) {
        for (auto [x, y, c] : XYC[i]) {
            // 頂点(i, cp[x])のout
            ll u = L + gridtoid(i, cp[x], cpsz);
            // 頂点(i + 1, cp[y])のin
            ll v = gridtoid(i + 1, cp[y], cpsz);
            mcf.add_edge(u, v, 1, c);
        }
    }
    rep(i, N) {
        rep(j, cpsz - 1) {
            // 同じ駅に留まることに当たる辺(流量は無制限)
            mcf.add_edge(L + gridtoid(i, j, cpsz), L + gridtoid(i, j + 1, cpsz), g, 0);
        }
    }
    auto [flow, cost] = mcf.flow(s, t);
    print({flow, cost});
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(15);

    // single test case
    // solve();

    // multi test cases
    while(1) solve();

    return 0;
}
accuracy: 1
time_ms: 890
memory_kb: 89560
final_rank: 16

code_snippet: // #pragma GCC target("avx2")
// #pragma GCC optimize("O3")
// #pragma GCC optimize("unroll-loops")

#define CONSTANTS
#pragma GCC diagnostic ignored "-Wunused-parameter"
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;

constexpr long long INF = 1e18;
// constexpr long long INF = LONG_LONG_MAX;
// constexpr int MOD = 1000000007;
constexpr int MOD = 998244353;
constexpr long double EPS = 1e-10;
constexpr long double PI = M_PI;

using ll = long long; using ull = unsigned long long; using ld = long double; using pll = pair<ll, ll>; using pii = pair<int, int>; using pli = pair<ll, int>; using pil = pair<int, ll>; template<typename T> using vv = vector<vector<T>>; using vvl = vv<ll>; using vvi = vv<int>; using vvpll = vv<pll>; using vvpli = vv<pli>; using vvpil = vv<pil>;
#define name4(i, a, b, c, d, e, ...) e
#define rep(...) name4(__VA_ARGS__, rep4, rep3, rep2, rep1)(__VA_ARGS__)
#define rep1(i, a) for (ll i = 0, _aa = a; i < _aa; i++)
#define rep2(i, a, b) for (ll i = a, _bb = b; i < _bb; i++)
#define rep3(i, a, b, c) for (ll i = a, _bb = b; (c > 0 && a <= i && i < _bb) or (c < 0 && a >= i && i > _bb); i += c)
#define rrep(i, a, b) for (ll i=(a); i>(b); i--)
#define pb push_back
#define eb emplace_back
#define mkp make_pair
#define ALL(A) begin(A), end(A)
#define UNIQUE(A) sort(ALL(A)), A.erase(unique(ALL(A)), A.end())
#define elif else if
#define tostr to_string
#ifndef CONSTANTS
constexpr ll INF = 1e18; constexpr int MOD = 1000000007; constexpr ld EPS = 1e-10; constexpr ld PI = M_PI;
#endif
template<typename T1, typename T2> istream &operator>>(istream &is, pair<T1, T2> &p) { is >> p.first >> p.second; return is; } template<typename T> istream &operator>>(istream &is, vector<T> &v) { for (T &in : v) is >> in; return is; } template<typename T = ll> vector<T> LIST(int N) { vector<T> A(N); rep(i, N) { cin >> A[i]; } return A; }
template<typename T> bool chmax(T &x, T y) { return (y > x) ? x = y, true : false; }
template<typename T1, typename T2> ostream &operator<<(ostream &os, const pair<T1, T2> &p) { return os << p.first << ' ' << p.second; } template<class T, size_t... Is> void print_tuple(ostream &os, const T &arg, index_sequence<Is...>) { static_cast<void>(((os << (Is == 0 ? "" : " "), os << get<Is>(arg)), ...)); } template<class... Ts> ostream &operator<<(ostream &os, const tuple<Ts...> &arg) { print_tuple(os, arg, make_index_sequence<sizeof...(Ts)>()); return os; } template<typename T, size_t N> ostream &operator<<(ostream &os, const array<T, N> &arr) { rep(i, N) { os << arr[i]; if (i != (int)N - 1) { os << ' '; } } return os; } template<typename T, size_t N> void print(const array<T, N> &arr, string sep = " ") { rep(i, N) { cout << arr[i]; if (i != (int)N - 1) cout << sep; } cout << '\n'; } template<typename T> ostream &operator<<(ostream &os, const vector<T> &vec) { rep(i, vec.size()) { os << vec[i]; if (i != (int)vec.size() - 1) { os << ' '; } } return os; } template<typename T> void print(const vector<T> &vec, string sep = " ") { rep(i, vec.size()) { cout << vec[i]; if (i != (int)vec.size() - 1) cout << sep; } cout << '\n'; } template<typename T> void print(const deque<T> &que, string sep = " ") { vector<T> vec(ALL(que)); print(vec, sep); } template<typename T> void print(const set<T> &se, string sep = " ") { vector<T> vec(ALL(se)); print(vec, sep); } template<typename T> void print(const initializer_list<T> &li, string sep = " ") { vector<T> V(ALL(li)); print(V, sep); } void print() { cout << '\n'; } template<typename T> void print(T out) { cout << out << '\n'; }
#define debug(...) multi_debug(#__VA_ARGS__, __VA_ARGS__)
template<typename T> void multi_debug(string name, const vv<T> &grid) { cerr << name << ":" << endl; int H = grid.size(); int W = grid[0].size(); vector<int> mxlen(W); rep(h, H) { rep(w, W) { chmax(mxlen[w], (int)tostr(grid[h][w]).size()); } } rep(h, H) { rep(w, W) { int pad = mxlen[w] - (int)tostr(grid[h][w]).size(); cerr << string(pad, ' ') << grid[h][w]; if (w == W - 1) { cerr << endl; } else { cerr << ' '; } } } } template<class Tp, class... Ts> void multi_debug(string names, Tp arg, Ts... args) { if constexpr (sizeof...(Ts) == 0) { while (names.back() == ' ') { names.pop_back(); } cerr << names << ": " << arg << endl; } else { int n = names.size(), comma_pos = -1, paren_depth = 0, inside_quote = false; rep(i, n) { if (not inside_quote and paren_depth == 0 and names[i] == ',') { comma_pos = i; break; } if (names[i] == '\"' or names[i] == '\'') { if (i > 0 and names[i - 1] == '\\') continue; inside_quote ^= true; } if (not inside_quote and names[i] == '(') { paren_depth++; } if (not inside_quote and names[i] == ')') { paren_depth--; } } assert(comma_pos != -1); string name = names.substr(0, comma_pos); while (name.back() == ' ') { name.pop_back(); } cerr << name << ": " << arg << endl; int next_begin_at = comma_pos + 1; while (names[next_begin_at] == ' ') { next_begin_at++; } names = names.substr(next_begin_at); multi_debug(names, args...); } } template<typename T> void print(const vector<T> &V, char sep) { print(V, string{sep}); } template<typename T, size_t N> void print(const array<T, N> &arr, char sep) { print(arr, string{sep}); }

template<typename T> int bisect_left(const vector<T> &A, T val, int lo = 0) { return lower_bound(A.begin() + lo, A.end(), val) - A.begin(); } template<typename T> int bisect_right(const vector<T> &A, T val, int lo = 0) { return upper_bound(A.begin() + lo, A.end(), val) - A.begin(); }
template<typename T> struct Compress { int N; vector<T> dat; bool built = false; Compress() {} Compress(const vector<T> &A) : dat(A) { build(); } void build() { sort(dat.begin(), dat.end()); dat.erase(unique(dat.begin(), dat.end()), dat.end()); N = dat.size(); built = true; } void add(T x) { assert(not built); dat.emplace_back(x); } template<typename... Ts> void add(const T val, Ts... ts) { dat.emplace_back(val); if constexpr (sizeof...(Ts) > 0) { add(ts...); } } void add(const vector<T> &A) { for (auto a : A) { add(a); } } int zip(T x) { assert(built); return bisect_left(dat, x); } T unzip(int x) { assert(built); return dat[x]; } int operator[](T x) { return zip(x); } int size() { assert(built); return dat.size(); } T size(int i) { assert(built and 0 <= i and i < N); if (i == N - 1) { return 1; } else { return unzip(i + 1) - unzip(i); } } vector<T> zip(const vector<T> &A) { int M = A.size(); vector<T> res(M); for (int i = 0; i < M; i++) { res[i] = zip(A[i]); } return res; } };
namespace atcoder { namespace internal { template <class E> struct csr { std::vector<int> start; std::vector<E> elist; explicit csr(int n, const std::vector<std::pair<int, E>>& edges) : start(n + 1), elist(edges.size()) { for (auto e : edges) { start[e.first + 1]++; } for (int i = 1; i <= n; i++) { start[i] += start[i - 1]; } auto counter = start; for (auto e : edges) { elist[counter[e.first]++] = e.second; } } }; } }
namespace atcoder { namespace internal { template <class T> struct simple_queue { std::vector<T> payload; int pos = 0; void reserve(int n) { payload.reserve(n); } int size() const { return int(payload.size()) - pos; } bool empty() const { return pos == int(payload.size()); } void push(const T& t) { payload.push_back(t); } T& front() { return payload[pos]; } void clear() { payload.clear(); pos = 0; } void pop() { pos++; } }; } }
namespace atcoder { template <class Cap, class Cost> struct mcf_graph { public: mcf_graph() {} explicit mcf_graph(int n) : _n(n) {} int add_edge(int from, int to, Cap cap, Cost cost) { assert(0 <= from && from < _n); assert(0 <= to && to < _n); assert(0 <= cap); assert(0 <= cost); int m = int(_edges.size()); _edges.push_back({from, to, cap, 0, cost}); return m; } struct edge { int from, to; Cap cap, flow; Cost cost; }; edge get_edge(int i) { int m = int(_edges.size()); assert(0 <= i && i < m); return _edges[i]; } std::vector<edge> edges() { return _edges; } std::pair<Cap, Cost> flow(int s, int t) { return flow(s, t, std::numeric_limits<Cap>::max()); } std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) { return slope(s, t, flow_limit).back(); } std::vector<std::pair<Cap, Cost>> slope(int s, int t) { return slope(s, t, std::numeric_limits<Cap>::max()); } std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) { assert(0 <= s && s < _n); assert(0 <= t && t < _n); assert(s != t); int m = int(_edges.size()); std::vector<int> edge_idx(m); auto g = [&]() { std::vector<int> degree(_n), redge_idx(m); std::vector<std::pair<int, _edge>> elist; elist.reserve(2 * m); for (int i = 0; i < m; i++) { auto e = _edges[i]; edge_idx[i] = degree[e.from]++; redge_idx[i] = degree[e.to]++; elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}}); elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}}); } auto _g = internal::csr<_edge>(_n, elist); for (int i = 0; i < m; i++) { auto e = _edges[i]; edge_idx[i] += _g.start[e.from]; redge_idx[i] += _g.start[e.to]; _g.elist[edge_idx[i]].rev = redge_idx[i]; _g.elist[redge_idx[i]].rev = edge_idx[i]; } return _g; }(); auto result = slope(g, s, t, flow_limit); for (int i = 0; i < m; i++) { auto e = g.elist[edge_idx[i]]; _edges[i].flow = _edges[i].cap - e.cap; } return result; } private: int _n; std::vector<edge> _edges; struct _edge { int to, rev; Cap cap; Cost cost; }; std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g, int s, int t, Cap flow_limit) { std::vector<std::pair<Cost, Cost>> dual_dist(_n); std::vector<int> prev_e(_n); std::vector<bool> vis(_n); struct Q { Cost key; int to; bool operator<(Q r) const { return key > r.key; } }; std::vector<int> que_min; std::vector<Q> que; auto dual_ref = [&]() { for (int i = 0; i < _n; i++) { dual_dist[i].second = std::numeric_limits<Cost>::max(); } std::fill(vis.begin(), vis.end(), false); que_min.clear(); que.clear(); size_t heap_r = 0; dual_dist[s].second = 0; que_min.push_back(s); while (!que_min.empty() || !que.empty()) { int v; if (!que_min.empty()) { v = que_min.back(); que_min.pop_back(); } else { while (heap_r < que.size()) { heap_r++; std::push_heap(que.begin(), que.begin() + heap_r); } v = que.front().to; std::pop_heap(que.begin(), que.end()); que.pop_back(); heap_r--; } if (vis[v]) continue; vis[v] = true; if (v == t) break; Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second; for (int i = g.start[v]; i < g.start[v + 1]; i++) { auto e = g.elist[i]; if (!e.cap) continue; Cost cost = e.cost - dual_dist[e.to].first + dual_v; if (dual_dist[e.to].second - dist_v > cost) { Cost dist_to = dist_v + cost; dual_dist[e.to].second = dist_to; prev_e[e.to] = e.rev; if (dist_to == dist_v) { que_min.push_back(e.to); } else { que.push_back(Q{dist_to, e.to}); } } } } if (!vis[t]) { return false; } for (int v = 0; v < _n; v++) { if (!vis[v]) continue; dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second; } return true; }; Cap flow = 0; Cost cost = 0, prev_cost_per_flow = -1; std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}}; while (flow < flow_limit) { if (!dual_ref()) break; Cap c = flow_limit - flow; for (int v = t; v != s; v = g.elist[prev_e[v]].to) { c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap); } for (int v = t; v != s; v = g.elist[prev_e[v]].to) { auto& e = g.elist[prev_e[v]]; e.cap += c; g.elist[e.rev].cap -= c; } Cost d = -dual_dist[s].first; flow += c; cost += c * d; if (prev_cost_per_flow == d) { result.pop_back(); } result.push_back({flow, cost}); prev_cost_per_flow = d; } return result; } }; }
template<typename T> bool chmin(T &x, T y) { return (y < x) ? x = y, true : false; }
template<typename T> vector<T> bellman_ford(int N, const vector<tuple<int, int, T>> &edges, int src) { vector<T> res(N, INF); res[src] = 0; rep(_, N - 1) { for (auto [u, v, cost] : edges) { if (res[u] == INF) { continue; } chmin(res[v], res[u] + cost); } } auto tmp = res; rep(_, N - 1) { for (auto [u, v, cost] : edges) { if (res[u] == INF) { continue; } if (res[v] > res[u] + cost) { res[v] = -INF; } } } rep(i, N) { if (tmp[i] != res[i]) { res[i] = -INF; } } return res; }
template<typename Cap, typename Cost> struct MinCostFlow : atcoder::mcf_graph<Cap, Cost> { using base_type = atcoder::mcf_graph<Cap, Cost>; int N; bool has_negative = false; vector<tuple<int, int, Cost>> temp_edges; vector<Cap> caps; vector<Cost> potential; MinCostFlow(int N) : base_type(N), N(N), has_negative(false) { potential.assign(N, 0); } int add_edge(int from, int to, Cap cap, Cost cost) { if (cost < 0) { has_negative = true; } int m = temp_edges.size(); temp_edges.emplace_back(from, to, cost); caps.emplace_back(cap); return m; } void build(int s) { if (has_negative) { potential = bellman_ford(N, temp_edges, s); for (int i = 0; i < temp_edges.size(); i++) { if (potential[i] >= INF) { potential[i] = 0; } assert(potential[i] > -INF); } } for (int i = 0; i < temp_edges.size(); i++) { auto [from, to, cost] = temp_edges[i]; base_type::add_edge(from, to, caps[i], cost + potential[from] - potential[to]); } } pair<Cap, Cost> flow(int s, int t) { return flow(s, t, numeric_limits<Cap>::max()); } pair<Cap, Cost> flow(int s, int t, Cap flow_limit) { return slope(s, t, flow_limit).back(); } vector<pair<Cap, Cost>> slope(int s, int t) { return slope(s, t, numeric_limits<Cap>::max()); } vector<pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) { build(s); auto result = base_type::slope(s, t, flow_limit); if (has_negative) { for (auto &[flow, cost] : result) { cost += (potential[t] - potential[s]) * flow; } } return result; } };
ll gridtoid(ll i, ll j, ll W) { return i * W + j; }

void solve() {
    ll N;
    cin >> N;
    if (N == 0) exit(0);
    Compress<ll> cp;
    vv<array<ll, 3>> XYC(N);
    rep(i, N - 1) {
        ll m;
        cin >> m;
        rep(_, m) {
            ll x, y, c;
            cin >> x >> y >> c;
            XYC[i].pb({x, y, c});
            cp.add(x, y);
        }
    }
    cp.build();
    ll cpsz = cp.size();
    ll g;
    cin >> g;

    // 頂点はV[駅][経過時間]
    ll L = N * cpsz;
    // 始点は頂点(0, 0)のout
    ll s = L;
    // 終点は頂点(N - 1, cpsz - 1)のout
    ll t = L * 2 - 1;
    MinCostFlow<ll, ll> mcf(L * 2);
    // in -> out
    rep(i, L) {
        // 流量1にして同時到着を防ぐ
        mcf.add_edge(i, L + i, 1, 0);
    }
    rep(i, N - 1) {
        for (auto [x, y, c] : XYC[i]) {
            // 頂点(i, cp[x])のout
            ll u = L + gridtoid(i, cp[x], cpsz);
            // 頂点(i + 1, cp[y])のin
            ll v = gridtoid(i + 1, cp[y], cpsz);
            mcf.add_edge(u, v, 1, c);
        }
    }
    rep(i, N) {
        rep(j, cpsz - 1) {
            // 同じ駅に留まることに当たる辺(流量は無制限)
            mcf.add_edge(L + gridtoid(i, j, cpsz), L + gridtoid(i, j + 1, cpsz), g, 0);
        }
    }
    auto [flow, cost] = mcf.flow(s, t, g);
    print({flow, cost});
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(15);

    // single test case
    // solve();

    // multi test cases
    while(1) solve();

    return 0;
}
accuracy: 1
time_ms: 900
memory_kb: 89308
final_rank: 17

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;


struct MCF {
private:
    const long long inf = 1e18;
    vector<long long> dist;       //最短距離(費用)
    vector<int> pv, pe;     //直前の頂点、辺    

public:
    struct edge {
        int to, rev; long long cap, cost;
        edge(int t = 0, int r = 0, long long ca = 0, long long co = 0)
            : to(t), rev(r), cap(ca), cost(co) {}
    };

    struct pii {
        long long d; int v; //最短距離(費用)、頂点番号
        pii(long long d_ = 0, int v_ = 0) : d(d_), v(v_) {}

        bool operator<(const pii& other) const {
            if (d != other.d) return d < other.d;
            return v < other.v;
        }

        bool operator>(const pii& other) const {
            if (d != other.d) return d > other.d;
            return v > other.v;
        }
    };

    int n;
    vector<vector<edge>> to;
    vector<long long> pot;        //ポテンシャル

    MCF(int n_ = 1) : n(n_), to(n_), pot(n_), dist(n_), pv(n_), pe(n_) {}

    void addedge(int u, int v, long long cap, long long cost) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap, cost });
        to[v].push_back({ u, su, 0, -cost });
    }

    //s->tへ流量fの最小費用流、流せないときは費用と流せなかった流量のペア
    pair<long long, long long> mincostflow(int s, int t, long long f) {
        long long res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return { res, f }; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;
            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return { res, f };
    }
};

int main()
{
    int n;
    while (cin >> n, n) {
        vi<int> m(n - 1);
        vii<int> x(n - 1), y(n - 1), c(n - 1);
        int sm = 0;
        rep(i, n - 1) {
            cin >> m[i];
            sm += m[i];
            rep(j, m[i]) {
                int tx, ty, tc;
                cin >> tx >> ty >> tc;
                x[i].push_back(tx);
                y[i].push_back(ty);
                c[i].push_back(tc);
            }
        }
        int g;
        cin >> g;

        MCF mcf(4 * sm + 2);
        int S = 4 * sm, T = S + 1;
        const ll inf = 100; //クラスは最大10個

        map<int, int> mp;
        int idx = 0;
        rep(i, n) {
            mp.clear();
            if (i > 0) rep(j, m[i - 1]) mp[y[i - 1][j]];
            if (i < n - 1) rep(j, m[i]) mp[x[i][j]];

            for (auto& elem : mp) elem.second = idx++;

            set<int> st;
            if (i > 0) rep(j, m[i - 1]) {
                y[i - 1][j] = mp[y[i - 1][j]];
                if (st.count(y[i - 1][j])) continue;
                st.insert(y[i - 1][j]);
                mcf.addedge(y[i - 1][j] + 2 * sm, y[i - 1][j], 1, 0);
            }
            if (i < n - 1) rep(j, m[i]) x[i][j] = mp[x[i][j]];

            auto itr = mp.begin();
            while (next(itr) != mp.end()) {
                mcf.addedge(itr->second, next(itr)->second, inf, 0);
                itr++;
            }
        }

        rep(i, n - 1) {
            if (i == 0) rep(j, m[i]) mcf.addedge(S, x[i][j], 1, 0); 
            if (i == n - 2) rep(j, m[i]) mcf.addedge(y[i][j], T, 1, 0);

            //xからy(倍加頂点側)への辺
            rep(j, m[i]) mcf.addedge(x[i][j], y[i][j] + 2 * sm, 1, c[i][j]);
        }

        pair<ll, ll> ans;
        ans = mcf.mincostflow(S, T, g);
        cout << g - ans.second << " " << ans.first << endl;

    }

    return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 4072
final_rank: 5

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;


struct MCF {
private:
    const long long inf = 1e18;
    vector<long long> dist;       //最短距離(費用)
    vector<int> pv, pe;     //直前の頂点、辺    

public:
    struct edge {
        int to, rev; long long cap, cost;
        edge(int t = 0, int r = 0, long long ca = 0, long long co = 0)
            : to(t), rev(r), cap(ca), cost(co) {}
    };

    struct pii {
        long long d; int v; //最短距離(費用)、頂点番号
        pii(long long d_ = 0, int v_ = 0) : d(d_), v(v_) {}

        bool operator<(const pii& other) const {
            if (d != other.d) return d < other.d;
            return v < other.v;
        }

        bool operator>(const pii& other) const {
            if (d != other.d) return d > other.d;
            return v > other.v;
        }
    };

    int n;
    vector<vector<edge>> to;
    vector<long long> pot;        //ポテンシャル

    MCF(int n_ = 1) : n(n_), to(n_), pot(n_), dist(n_), pv(n_), pe(n_) {}

    void addedge(int u, int v, long long cap, long long cost) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap, cost });
        to[v].push_back({ u, su, 0, -cost });
    }

    //s->tへ流量fの最小費用流、流せないときは-1
    long long mincostflow(int s, int t, long long f) {
        long long res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return -1; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;
            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return res;
    }
};

int main()
{
    int n;
    while (cin >> n, n) {
        vi<int> m(n - 1);
        vii<int> x(n - 1), y(n - 1), c(n - 1);
        int sm = 0;
        rep(i, n - 1) {
            cin >> m[i];
            sm += m[i];
            rep(j, m[i]) {
                int tx, ty, tc;
                cin >> tx >> ty >> tc;
                x[i].push_back(tx);
                y[i].push_back(ty);
                c[i].push_back(tc);
            }
        }
        int g;
        cin >> g;

        MCF mcf(4 * sm + 2);
        int S = 4 * sm, T = S + 1;
        const ll inf = 100; //クラスは最大10個

        map<int, int> mp;
        int idx = 0;
        rep(i, n) {
            mp.clear();
            if (i > 0) rep(j, m[i - 1]) mp[y[i - 1][j]];
            if (i < n - 1) rep(j, m[i]) mp[x[i][j]];

            for (auto& elem : mp) elem.second = idx++;

            set<int> st;
            if (i > 0) rep(j, m[i - 1]) {
                y[i - 1][j] = mp[y[i - 1][j]];
                if (st.count(y[i - 1][j])) continue;
                st.insert(y[i - 1][j]);
                mcf.addedge(y[i - 1][j] + 2 * sm, y[i - 1][j], 1, 0);
            }
            if (i < n - 1) rep(j, m[i]) x[i][j] = mp[x[i][j]];

            auto itr = mp.begin();
            while (next(itr) != mp.end()) {
                mcf.addedge(itr->second, next(itr)->second, inf, 0);
                itr++;
            }
        }

        rep(i, n - 1) {
            set<int> st;
            if (i == 0) rep(j, m[i]) mcf.addedge(S, x[i][j], 1, 0); 
            if (i == n - 2) rep(j, m[i]) mcf.addedge(y[i][j], T, 1, 0);

            //xからy(倍加頂点側)への辺
            rep(j, m[i]) mcf.addedge(x[i][j], y[i][j] + 2 * sm, 1, c[i][j]);
        }

        ll cost = 0;
        for (int flow = 1; flow <= g; flow++) {
            ll tc = mcf.mincostflow(S, T, 1);
            if (tc < 0) {
                cout << flow - 1 << " " << cost << endl;
                break;
            }
            cost += tc;
            if (flow == g) cout << flow << " " << cost << endl; //here?
        }

    }

    return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 4360
final_rank: 4

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;


struct MCF {
private:
    const long long inf = 1e18;
    vector<long long> dist;       //最短距離(費用)
    vector<int> pv, pe;     //直前の頂点、辺    

public:
    struct edge {
        int to, rev; long long cap, cost;
        edge(int t = 0, int r = 0, long long ca = 0, long long co = 0)
            : to(t), rev(r), cap(ca), cost(co) {}
    };

    struct pii {
        long long d; int v; //最短距離(費用)、頂点番号
        pii(long long d_ = 0, int v_ = 0) : d(d_), v(v_) {}

        bool operator<(const pii& other) const {
            if (d != other.d) return d < other.d;
            return v < other.v;
        }

        bool operator>(const pii& other) const {
            if (d != other.d) return d > other.d;
            return v > other.v;
        }
    };

    int n;
    vector<vector<edge>> to;
    vector<long long> pot;        //ポテンシャル

    MCF(int n_ = 1) : n(n_), to(n_), pot(n_), dist(n_), pv(n_), pe(n_) {}

    void addedge(int u, int v, long long cap, long long cost) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap, cost });
        to[v].push_back({ u, su, 0, -cost });
    }

    //s->tへ流量fの最小費用流、流せないときは-1
    long long mincostflow(int s, int t, long long f) {
        long long res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return -1; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;
            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return res;
    }
};

int main()
{
    int n;
    while (cin >> n, n) {
        vi<int> m(n - 1);
        vii<int> x(n - 1), y(n - 1), c(n - 1);
        int sm = 0;
        rep(i, n - 1) {
            cin >> m[i];
            sm += m[i];
            rep(j, m[i]) {
                int tx, ty, tc;
                cin >> tx >> ty >> tc;
                x[i].push_back(tx);
                y[i].push_back(ty);
                c[i].push_back(tc);
            }
        }
        int g;
        cin >> g;

        MCF mcf(4 * sm + 2);
        int S = 4 * sm, T = S + 1;
        const ll inf = 100; //クラスは最大10個

        map<int, int> mp;
        int idx = 0;
        rep(i, n) {
            mp.clear();
            if (i > 0) rep(j, m[i - 1]) mp[y[i - 1][j]];
            if (i < n - 1) rep(j, m[i]) mp[x[i][j]];

            for (auto& elem : mp) elem.second = idx++;

            set<int> st;
            if (i > 0) rep(j, m[i - 1]) {
                y[i - 1][j] = mp[y[i - 1][j]];
                if (st.count(y[i - 1][j])) continue;
                st.insert(y[i - 1][j]);
                mcf.addedge(y[i - 1][j] + 2 * sm, y[i - 1][j], 1, 0);
            }
            if (i < n - 1) rep(j, m[i]) x[i][j] = mp[x[i][j]];

            auto itr = mp.begin();
            while (next(itr) != mp.end()) {
                mcf.addedge(itr->second, next(itr)->second, inf, 0);
                itr++;
            }
        }

        rep(i, n - 1) {
            set<int> st;
            if (i == 0) rep(j, m[i]) mcf.addedge(S, x[i][j], 1, 0); 
            if (i == n - 2) rep(j, m[i]) {
                if(st.count(y[i][j])) continue;
                st.insert(y[i][j]);
                mcf.addedge(y[i][j], T, 1, 0);
            }

            //xからy(倍加頂点側)への辺
            rep(j, m[i]) mcf.addedge(x[i][j], y[i][j] + 2 * sm, 1, c[i][j]);
        }

        ll cost = 0;
        for (int flow = 1; flow <= g; flow++) {
            ll tc = mcf.mincostflow(S, T, 1);
            if (tc < 0) {
                cout << flow - 1 << " " << cost << endl;
                break;
            }
            cost += tc;
            if (flow == g) cout << flow << " " << cost << endl; //here?
        }

    }

    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 4292
final_rank: 3

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;


struct MCF {
private:
    const long long inf = 1e18;
    vector<long long> dist;       //最短距離(費用)
    vector<int> pv, pe;     //直前の頂点、辺    

public:
    struct edge {
        int to, rev; long long cap, cost;
        edge(int t = 0, int r = 0, long long ca = 0, long long co = 0)
            : to(t), rev(r), cap(ca), cost(co) {}
    };

    struct pii {
        long long d; int v; //最短距離(費用)、頂点番号
        pii(long long d_ = 0, int v_ = 0) : d(d_), v(v_) {}

        bool operator<(const pii& other) const {
            if (d != other.d) return d < other.d;
            return v < other.v;
        }

        bool operator>(const pii& other) const {
            if (d != other.d) return d > other.d;
            return v > other.v;
        }
    };

    int n;
    vector<vector<edge>> to;
    vector<long long> pot;        //ポテンシャル

    MCF(int n_ = 1) : n(n_), to(n_), pot(n_), dist(n_), pv(n_), pe(n_) {}

    void addedge(int u, int v, long long cap, long long cost) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap, cost });
        to[v].push_back({ u, su, 0, -cost });
    }

    //s->tへ流量fの最小費用流、流せないときは-1
    long long mincostflow(int s, int t, long long f) {
        long long res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return -1; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;
            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return res;
    }
};

int main()
{
    int n;
    while (cin >> n, n) {
        vi<int> m(n - 1);
        vii<int> x(n - 1), y(n - 1), c(n - 1);
        int sm = 0;
        rep(i, n - 1) {
            cin >> m[i];
            sm += m[i];
            rep(j, m[i]) {
                int tx, ty, tc;
                cin >> tx >> ty >> tc;
                x[i].push_back(tx);
                y[i].push_back(ty);
                c[i].push_back(tc);
            }
        }
        int g;
        cin >> g;

        MCF mcf(4 * sm + 2);
        int S = 4 * sm, T = S + 1;
        const ll inf = 100; //クラスは最大10個

        map<int, int> mp;
        int idx = 0;
        rep(i, n) {
            mp.clear();
            if (i > 0) rep(j, m[i - 1]) mp[y[i - 1][j]];
            if (i < n - 1) rep(j, m[i]) mp[x[i][j]];

            for (auto& elem : mp) elem.second = idx++;

            set<int> st;
            if (i > 0) rep(j, m[i - 1]) {
                y[i - 1][j] = mp[y[i - 1][j]];
                if (st.count(y[i - 1][j])) continue;
                st.insert(y[i - 1][j]);
                mcf.addedge(y[i - 1][j] + 2 * sm, y[i - 1][j], 1, 0);
            }
            if (i < n - 1) rep(j, m[i]) x[i][j] = mp[x[i][j]];

            auto itr = mp.begin();
            while (next(itr) != mp.end()) {
                mcf.addedge(itr->second, next(itr)->second, inf, 0);
                itr++;
            }
        }

        rep(i, n - 1) {
            set<int> st;
            if (i == 0) rep(j, m[i]) mcf.addedge(S, x[i][j], 1, 0); 
            if (i == n - 2) rep(j, m[i]) {
                if(st.count(y[i][j])) continue;
                st.insert(y[i][j]);
                mcf.addedge(y[i][j], T, 1, 0);
            }

            //xからy(倍加頂点側)への辺
            rep(j, m[i]) mcf.addedge(x[i][j], y[i][j] + 2 * sm, 1, c[i][j]);
        }

        ll cost = 0;
        for (int flow = 1; flow <= g; flow++) {
            ll tc = mcf.mincostflow(S, T, 1);
            if (tc < 0) {
                cout << flow - 1 << " " << cost << endl;
                break;
            }
            cost += tc;
        }

    }

    return 0;
}
accuracy: 0.3333333333333333
time_ms: 40
memory_kb: 4184
final_rank: 19

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;


struct MCF {
private:
    const long long inf = 1e18;
    vector<long long> dist;       //最短距離(費用)
    vector<int> pv, pe;     //直前の頂点、辺    

public:
    struct edge {
        int to, rev; long long cap, cost;
        edge(int t = 0, int r = 0, long long ca = 0, long long co = 0)
            : to(t), rev(r), cap(ca), cost(co) {}
    };

    struct pii {
        long long d; int v; //最短距離(費用)、頂点番号
        pii(long long d_ = 0, int v_ = 0) : d(d_), v(v_) {}

        bool operator<(const pii& other) const {
            if (d != other.d) return d < other.d;
            return v < other.v;
        }

        bool operator>(const pii& other) const {
            if (d != other.d) return d > other.d;
            return v > other.v;
        }
    };

    int n;
    vector<vector<edge>> to;
    vector<long long> pot;        //ポテンシャル

    MCF(int n_ = 1) : n(n_), to(n_), pot(n_), dist(n_), pv(n_), pe(n_) {}

    void addedge(int u, int v, long long cap, long long cost) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap, cost });
        to[v].push_back({ u, su, 0, -cost });
    }

    //s->tへ流量fの最小費用流、流せないときは-1
    long long mincostflow(int s, int t, long long f) {
        long long res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return -1; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;
            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return res;
    }
};

int main()
{
    int n;
    while (cin >> n, n) {
        vi<int> m(n - 1);
        vii<int> x(n - 1), y(n - 1), c(n - 1);
        int sm = 0;
        rep(i, n - 1) {
            cin >> m[i];
            sm += m[i];
            rep(j, m[i]) {
                int tx, ty, tc;
                cin >> tx >> ty >> tc;
                x[i].push_back(tx);
                y[i].push_back(ty);
                c[i].push_back(tc);
            }
        }
        int g;
        cin >> g;

        MCF mcf(4 * sm + 2);
        int S = 4 * sm, T = S + 1;
        const ll inf = 100; //クラスは最大10個

        map<int, int> mp;
        int idx = 0;
        rep(i, n) {
            mp.clear();
            if (i > 0) rep(j, m[i - 1]) mp[y[i - 1][j]];
            if (i < n - 1) rep(j, m[i]) mp[x[i][j]];

            for (auto& elem : mp) elem.second = idx++;

            set<int> st;
            if (i > 0) rep(j, m[i - 1]) {
                y[i - 1][j] = mp[y[i - 1][j]];
                if (st.count(y[i - 1][j])) continue;
                st.insert(y[i - 1][j]);
                mcf.addedge(y[i - 1][j] + 2 * sm, y[i - 1][j], 1, 0);
            }
            if (i < n - 1) rep(j, m[i]) x[i][j] = mp[x[i][j]];

            auto itr = mp.begin();
            while (next(itr) != mp.end()) {
                mcf.addedge(itr->second, next(itr)->second, inf, 0);
                itr++;
            }
        }

        rep(i, n - 1) {
            set<int> st;
            if (i == 0) rep(j, m[i]) mcf.addedge(S, x[i][j], 1, 0); 
            if (i == n - 2) rep(j, m[i]) {
                //if(st.count(y[i][j])) continue;
                //st.insert(y[i][j]);
                mcf.addedge(y[i][j], T, 1, 0);
            }

            //xからy(倍加頂点側)への辺
            rep(j, m[i]) mcf.addedge(x[i][j], y[i][j] + 2 * sm, 1, c[i][j]);
        }

        ll cost = 0;
        for (int flow = 1; flow <= g; flow++) {
            ll tc = mcf.mincostflow(S, T, 1);
            if (tc < 0) {
                cout << flow - 1 << " " << cost << endl;
                break;
            }
            cost += tc;
        }

    }

    return 0;
}
accuracy: 0.3333333333333333
time_ms: 40
memory_kb: 4196
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define repd(i,a,b) for (ll i=(a);i<(b);i++)
#define rep(i,n) repd(i,0,n)
#define all(x) (x).begin(),(x).end()
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return true; } return false; }
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return true; } return false; }
typedef long long ll;
typedef pair<ll,ll> P;
typedef vector<ll> vec;
typedef vector<vec> Graph;
const long long INF = 1LL<<60;
const long long MOD = 1000000007;

//https://github.com/atcoder/ac-library
#ifndef ATCODER_INTERNAL_CSR_HPP
#define ATCODER_INTERNAL_CSR_HPP 1

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_CSR_HPP

#ifndef ATCODER_INTERNAL_QUEUE_HPP
#define ATCODER_INTERNAL_QUEUE_HPP 1

#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_QUEUE_HPP

#ifndef ATCODER_MINCOSTFLOW_HPP
#define ATCODER_MINCOSTFLOW_HPP 1

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    explicit mcf_graph(int n) : _n(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(_edges.size());
        _edges.push_back({from, to, cap, 0, cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(_edges.size());
        assert(0 <= i && i < m);
        return _edges[i];
    }
    std::vector<edge> edges() { return _edges; }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        int m = int(_edges.size());
        std::vector<int> edge_idx(m);

        auto g = [&]() {
            std::vector<int> degree(_n), redge_idx(m);
            std::vector<std::pair<int, _edge>> elist;
            elist.reserve(2 * m);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] = degree[e.from]++;
                redge_idx[i] = degree[e.to]++;
                elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}});
                elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}});
            }
            auto _g = internal::csr<_edge>(_n, elist);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] += _g.start[e.from];
                redge_idx[i] += _g.start[e.to];
                _g.elist[edge_idx[i]].rev = redge_idx[i];
                _g.elist[redge_idx[i]].rev = edge_idx[i];
            }
            return _g;
        }();

        auto result = slope(g, s, t, flow_limit);

        for (int i = 0; i < m; i++) {
            auto e = g.elist[edge_idx[i]];
            _edges[i].flow = _edges[i].cap - e.cap;
        }

        return result;
    }

  private:
    int _n;
    std::vector<edge> _edges;

    // inside edge
    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
                                            int s,
                                            int t,
                                            Cap flow_limit) {
        // variants (C = maxcost):
        // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
        // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge

        // dual_dist[i] = (dual[i], dist[i])
        std::vector<std::pair<Cost, Cost>> dual_dist(_n);
        std::vector<int> prev_e(_n);
        std::vector<bool> vis(_n);
        struct Q {
            Cost key;
            int to;
            bool operator<(Q r) const { return key > r.key; }
        };
        std::vector<int> que_min;
        std::vector<Q> que;
        auto dual_ref = [&]() {
            for (int i = 0; i < _n; i++) {
                dual_dist[i].second = std::numeric_limits<Cost>::max();
            }
            std::fill(vis.begin(), vis.end(), false);
            que_min.clear();
            que.clear();

            // que[0..heap_r) was heapified
            size_t heap_r = 0;

            dual_dist[s].second = 0;
            que_min.push_back(s);
            while (!que_min.empty() || !que.empty()) {
                int v;
                if (!que_min.empty()) {
                    v = que_min.back();
                    que_min.pop_back();
                } else {
                    while (heap_r < que.size()) {
                        heap_r++;
                        std::push_heap(que.begin(), que.begin() + heap_r);
                    }
                    v = que.front().to;
                    std::pop_heap(que.begin(), que.end());
                    que.pop_back();
                    heap_r--;
                }
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                // dist[v] = shortest(s, v) + dual[s] - dual[v]
                // dist[v] >= 0 (all reduced cost are positive)
                // dist[v] <= (n-1)C
                Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                    auto e = g.elist[i];
                    if (!e.cap) continue;
                    // |-dual[e.to] + dual[v]| <= (n-1)C
                    // cost <= C - -(n-1)C + 0 = nC
                    Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                    if (dual_dist[e.to].second - dist_v > cost) {
                        Cost dist_to = dist_v + cost;
                        dual_dist[e.to].second = dist_to;
                        prev_e[e.to] = e.rev;
                        if (dist_to == dist_v) {
                            que_min.push_back(e.to);
                        } else {
                            que.push_back(Q{dist_to, e.to});
                        }
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                // dual[v] = dual[v] - dist[t] + dist[v]
                //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) +
                //         (shortest(s, v) + dual[s] - dual[v]) = - shortest(s,
                //         t) + dual[t] + shortest(s, v) = shortest(s, v) -
                //         shortest(s, t) >= 0 - (n-1)C
                dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}};
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
            }
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                auto& e = g.elist[prev_e[v]];
                e.cap += c;
                g.elist[e.rev].cap -= c;
            }
            Cost d = -dual_dist[s].first;
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }
};

}  // namespace atcoder

#endif  // ATCODER_MINCOSTFLOW_HPP

using namespace atcoder;

int main()
{  
    ios::sync_with_stdio(false);
    cin.tie(0);
    ll n;
    while(cin>>n){
      if(n==0)break;
      mcf_graph<ll,ll> g(n*40+1);
      vector<ll> memo={0};
      rep(i,n-1){
        ll m;cin>>m;
        vector<ll> old;
        swap(old,memo);
        map<ll,ll> mp;
        rep(j,m){
          ll a,b,c;
          cin>>a>>b>>c;
          ll cj=j;
          if(mp.count(b)){
            cj=mp[b];
          }
          else{
            mp[b]=j;
          }
          rep(k,old.size()){
            ll x=old[k];
            if(x<=a){
              g.add_edge(20+40*i+k,40*(i+1)+cj,1,c);
            }
          }
          if(cj==j)g.add_edge(40*(i+1)+j,40*(i+1)+j+20,1,0);
          memo.push_back(b);
        }
      }
      rep(i,20){
        g.add_edge(40*(n-1)+20+i,40*n,1,0);
      }
      ll G;cin>>G;
      auto[x,y]=g.flow(20,40*n,G);
      cout<<x<<" "<<y<<endl;
    }
    return 0;
}
accuracy: 1
time_ms: 120
memory_kb: 10916
final_rank: 11

code_snippet: #include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>


#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder


#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    explicit mcf_graph(int n) : _n(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(_edges.size());
        _edges.push_back({from, to, cap, 0, cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(_edges.size());
        assert(0 <= i && i < m);
        return _edges[i];
    }
    std::vector<edge> edges() { return _edges; }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        int m = int(_edges.size());
        std::vector<int> edge_idx(m);

        auto g = [&]() {
            std::vector<int> degree(_n), redge_idx(m);
            std::vector<std::pair<int, _edge>> elist;
            elist.reserve(2 * m);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] = degree[e.from]++;
                redge_idx[i] = degree[e.to]++;
                elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}});
                elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}});
            }
            auto _g = internal::csr<_edge>(_n, elist);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] += _g.start[e.from];
                redge_idx[i] += _g.start[e.to];
                _g.elist[edge_idx[i]].rev = redge_idx[i];
                _g.elist[redge_idx[i]].rev = edge_idx[i];
            }
            return _g;
        }();

        auto result = slope(g, s, t, flow_limit);

        for (int i = 0; i < m; i++) {
            auto e = g.elist[edge_idx[i]];
            _edges[i].flow = _edges[i].cap - e.cap;
        }

        return result;
    }

  private:
    int _n;
    std::vector<edge> _edges;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
                                            int s,
                                            int t,
                                            Cap flow_limit) {

        std::vector<std::pair<Cost, Cost>> dual_dist(_n);
        std::vector<int> prev_e(_n);
        std::vector<bool> vis(_n);
        struct Q {
            Cost key;
            int to;
            bool operator<(Q r) const { return key > r.key; }
        };
        std::vector<int> que_min;
        std::vector<Q> que;
        auto dual_ref = [&]() {
            for (int i = 0; i < _n; i++) {
                dual_dist[i].second = std::numeric_limits<Cost>::max();
            }
            std::fill(vis.begin(), vis.end(), false);
            que_min.clear();
            que.clear();

            size_t heap_r = 0;

            dual_dist[s].second = 0;
            que_min.push_back(s);
            while (!que_min.empty() || !que.empty()) {
                int v;
                if (!que_min.empty()) {
                    v = que_min.back();
                    que_min.pop_back();
                } else {
                    while (heap_r < que.size()) {
                        heap_r++;
                        std::push_heap(que.begin(), que.begin() + heap_r);
                    }
                    v = que.front().to;
                    std::pop_heap(que.begin(), que.end());
                    que.pop_back();
                    heap_r--;
                }
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                    auto e = g.elist[i];
                    if (!e.cap) continue;
                    Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                    if (dual_dist[e.to].second - dist_v > cost) {
                        Cost dist_to = dist_v + cost;
                        dual_dist[e.to].second = dist_to;
                        prev_e[e.to] = e.rev;
                        if (dist_to == dist_v) {
                            que_min.push_back(e.to);
                        } else {
                            que.push_back(Q{dist_to, e.to});
                        }
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}};
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
            }
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                auto& e = g.elist[prev_e[v]];
                e.cap += c;
                g.elist[e.rev].cap -= c;
            }
            Cost d = -dual_dist[s].first;
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }
};

}  // namespace atcoder

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < n; ++i)
typedef long long ll;
using namespace atcoder;
using namespace std;
const int INF = 1e9;

int id() {
    static int id = -2;
    id += 2;
    return id;
}

void solve(int N) {
    vector<set<int>> st(N);
    vector edges(N - 1, vector<tuple<int, int, int>>());
    rep(i, N - 1) {
        int M;
        cin >> M;
        rep(j, M) {
            int x, y, c;
            cin >> x >> y >> c;
            st[i].insert(x);
            st[i + 1].insert(y);
            edges[i].emplace_back(x, y, c);
        }
    }

    // [駅, 時間] -> id
    map<pair<int, int>, int> toid;
    rep(i, N) {
        for (int x : st[i]) {
            toid[{i, x}] = id();
        }
    }

    int s = id(), t = id();
    int id_size = id();
    mcf_graph<int, int> G(id_size);

    // 駅のinout
    for (int i = 0; i < id_size; i += 2) G.add_edge(i, i + 1, 1, 0);

    // s -> 0番目の駅
    for (int x : st[0]) G.add_edge(s, toid[{0, x}] + 1, INF, 0);

    // 駅間
    rep(i, N - 1) {
        for (auto &[x, y, c] : edges[i]) {
            G.add_edge(toid[{i, x}] + 1, toid[{i + 1, y}], 1, c);
        }
    }

    // 最後の駅 -> t
    for (int x : st[N - 1]) G.add_edge(toid[{N - 1, x}] + 1, t, INF, 0);

    // 駅で待機
    rep(i, N) {
        for (int x : st[i]) {
            for (int y : st[i]) {
                if (x <= y)
                    G.add_edge(toid[{i, x}] + 1, toid[{i, y}] + 1, INF, 0);
            }
        }
    }

    int g;
    cin >> g;
    auto [max_flow, min_cost] = G.flow(s, t, g);
    cout << max_flow << " " << min_cost << endl;
}

int main() {
    cin.tie(0)->sync_with_stdio(0);

    int N;
    while (cin >> N, N) solve(N);

    return 0;
}
accuracy: 1
time_ms: 120
memory_kb: 10932
final_rank: 12

code_snippet: #include<bits/stdc++.h>
using namespace std;

struct mcf{
	struct edge{
		int from;
		int to;
		int rev;
		int cap;
		int cost;
	};
	int n;
	vector<edge> e;
	vector<vector<int>> ikeru;

	mcf(int _n) : n(_n) {
		ikeru.resize(n);
	}

	void add_edge(int from, int to, int cap, int cost){
		int x = e.size();
		e.push_back({from, to, x + 1, cap, cost});
		e.push_back({to, from, x, 0, -cost});
		ikeru[from].push_back(x);
		ikeru[to].push_back(x + 1);
	}

	pair<int,int> flow(int st, int gl, int flw){
		vector<int> p(n, 0);
		int ans1 = 0;
		int ans2 = 0;
		while(flw > 0){
			vector<int> d(n, 1e9);
			vector<int> c(n, -1);
			d[st] = 0;
			priority_queue<pair<int,int>> pq;
			pq.push(pair(0, st));
			while(!pq.empty()){
				auto [tmp, i] = pq.top();
				pq.pop();
				tmp = -tmp;
				if (tmp > d[i]) continue;
				for (int x: ikeru[i]){
					if (e[x].cap == 0) continue;
					int j = e[x].to;
					int targ = tmp + e[x].cost + p[i] - p[j];
					if (targ < d[j]){
						d[j] = targ;
						c[j] = x;
						pq.push(pair(-d[j], j));
					}
				}
			}
			if (d[gl] >= 1e9) break;
			for (int i=0; i<n; i++){
				p[i] += d[i];
			}
			int nw = gl;
			int f = flw;
			while(nw != st){
				f = min(f, e[c[nw]].cap);
				nw = e[c[nw]].from;
			}
			ans1 += f;
			flw -= f;
			ans2 += f * p[gl];
			nw = gl;
			while(nw != st){
				e[c[nw]].cap -= f;
				e[e[c[nw]].rev].cap += f;
				nw = e[c[nw]].from;
			}
		}
		return pair(ans1, ans2);
	}

};

void solve(int n){
	vector<vector<tuple<int,int,int>>> g(n-1, vector<tuple<int,int,int>>(0));
	vector<int> times;

	for (int i=0; i<n-1; i++){
		int m; cin >> m;
		for (int j=0; j<m; j++){
			int x, y, z; cin >> x >> y >> z;
			g[i].push_back(make_tuple(x, y, z));
			times.push_back(x);
			times.push_back(y);
		}
	}

	int f; cin >> f;
	sort(times.begin(), times.end());
	times.erase(unique(times.begin(), times.end()), times.end());
	int k = times.size();


	mcf flow(k*n*2 + 2);

	for (int i=0; i<n; i++){
		for (int j=0; j<k-1; j++){
			flow.add_edge(n*k+i*k+j, n*k+i*k+j+1, f, 0);
		}
	}

	for (int i=0; i<n-1; i++){
		for (int j=0; j<(int)g[i].size(); j++){
			flow.add_edge(
				n*k+i*k + lower_bound(times.begin(), times.end(), get<0>(g[i][j])) - times.begin(),
				(i+1)*k + lower_bound(times.begin(), times.end(), get<1>(g[i][j])) - times.begin(),
				1,
				get<2>(g[i][j])
			);
		}
	}

	for (int i=0; i<n*k; i++){
		flow.add_edge(i, n*k+i, 1, 0);
	}
	

	flow.add_edge(2*k*n, k*n, f, 0);
	flow.add_edge(2*k*n-1, 2*k*n+1, f, 0);
	
	auto [ans1, ans2] = flow.flow(2*k*n, 2*k*n+1, f);
	cout << ans1 << ' ' << ans2 << '\n';

}

int main(){
	while(true){
		int n; cin >> n;
		if (n == 0) return 0;
		solve(n);
	}
}
accuracy: 1
time_ms: 1200
memory_kb: 60020
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<ll, ll> pr;
typedef pair<pr, ll> prpl;
typedef pair<ll, pr> prlp;
typedef tuple<ll, ll, ll> tri;
typedef tuple<ll, ll, ll, ll, ll> cinq;


const ll INF = (ll)1e18;

#define rep(i, n) for (ll i = 0; i < (ll)(n); i++) // for文の省略
#define all(v) v.begin(), v.end() // 配列範囲の指定の省略。reverse()やsort()に入れる時便利
#define pb push_back

// 変数を宣言して入力を代入する所の省略
template<class... T>
void input(T&... a){
    (cin >> ... >> a);
}
#define llin(...) ll __VA_ARGS__; input(__VA_ARGS__)
void input_vector(vector<ll>& v, ll N){
    rep(i, N){
        cin >> v[i];
    }
}
#define vectin(v, N) vector<ll> v(N); input_vector(v, N)
template<class U>
void input_graph(vector<vector<U>>& G, ll M, bool directed = false, bool weighted = false){
    rep(i, M){
        if(!weighted){
            ll A, B; cin >> A >> B;
            if(!directed){
                G[A].push_back(B);
                G[B].push_back(A);
            } else {
                G[A].push_back(B);
            }
        } else {
            ll A, B, cost; cin >> A >> B >> cost;
            if(!directed){
                G[A].push_back({B, cost});
                G[B].push_back({A, cost});
            } else {
                G[A].push_back({B, cost});
            }
        }
    }
}
#define graphin(G, N, M) vector<vector<ll>> G(N); input_graph(G, M)
#define graphin_directed(G, N, M) vector<vector<ll>> G(N); input_graph(G, M, directed = true)
#define graphin_weighted(G, N, M) vector<vector<pr>> G(N); input_graph(G, M, weighted = true)
#define graphin_directed_weighted(G, N, M) vector<vector<pr>> G(N); input_graph(G, M, directed = true, weighted = true)
template<class U>
void input_edge(vector<U> E, ll M, bool weighted = false){
    rep(i, M){
        if(!weighted){
            ll A, B; cin >> A >> B;
            E[i] = {A, B};
        } else {
            ll A, B, cost; cin >> A >> B >> cost;
            E[i] = {cost, {A, B}};
        }
    }
    if(weighted){
        sort(all(E));
    }
}
#define edgein(E, M) vector<pr> E(M); input_edge(E, M)
#define edgein_weighted(E, M) vector<prlp> E(M); input_edge(E, M, weighted = true)


// デバッグ処理の簡略化。「#define _DEBUG」行をコメントアウトするかどうかで処理を行うかどうか変えられる
void debug_out() { cout << endl; }
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
  cout << H << " ";
  debug_out(T...);
}
#define _DEBUG
#ifdef _DEBUG
#define debug(...) debug_out(__VA_ARGS__)
#else
#define debug(...) 
#endif

#ifndef H_make_vector
#define H_make_vector

/**
 * @brief 多次元 vector の作成
 * @author えびちゃん
 */

#include <cstddef>
#include <type_traits>
#include <vector>

namespace detail {
  template <typename Tp, ll Nb>
  auto make_vector(std::vector<ll>& sizes, Tp const& x) {
    if constexpr (Nb == 1) {
      return vector(sizes[0], x);
    } else {
      ll size = sizes[Nb-1];
      sizes.pop_back();
      return vector(size, make_vector<Tp, Nb-1>(sizes, x));
    }
  }
}  // detail::

template <typename Tp, ll Nb>
auto make_vector(ll const(&sizes)[Nb], Tp const& x = Tp()) {
  vector<ll> s(Nb);
  for (ll i = 0; i < Nb; ++i) s[i] = sizes[Nb-i-1];
  return detail::make_vector<Tp, Nb>(s, x);
}

#endif  /* !defined(H_make_vector) */

template <typename S, typename T>
pair<S, T> operator+(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first + q.first, p.second + q.second);
}
 
template <typename S, typename T>
pair<S, T> operator-(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first - q.first, p.second - q.second);
}

ll Max_V = 6000;

struct edge{
    ll to, cap, cost, rev;
};
vector<vector<edge>> G(Max_V);

void add_edge(ll from, ll to, ll cap, ll cost){
    G[from].pb((edge){to, cap, cost, (ll)G[to].size()});
    G[to].pb((edge){from, 0, -cost, (ll)G[from].size() - 1});
}

ll min_cost_flow(vector<vector<edge>> G, ll s, ll t, ll f){
    vector<ll> h(Max_V), dist(Max_V), prevv(Max_V), preve(Max_V);
    ll res = 0;
    while(f > 0){
        priority_queue<pr, vector<pr>, greater<pr>> que;
        rep(i, Max_V){
            dist[i] = INF;
        }
        dist[s] = 0;
        que.push({0, s});
        while(!que.empty()){
            pr p = que.top(); que.pop();
            ll v = p.second;
            if(dist[v] < p.first) continue;
            rep(i, G[v].size()){
                edge &e = G[v][i];
                if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v; preve[e.to] = i;
                    que.push({dist[e.to], e.to});
                }
            }
        }
        if(dist[t] == INF){
            return -1;
        }
        rep(v, Max_V){
            h[v] += dist[v];
        }
        ll d = f;
        for(ll v = t; v != s; v = prevv[v]){
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d * h[t];
        for(ll v = t; v != s; v = prevv[v]){
            edge &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

ll find_aug_path_DFS(vector<bool> &used, vector<vector<edge>> &G, ll v, ll t, ll f){
    if(v == t) return f;
    used[v] = true;
    rep(i, G[v].size()){
        edge& e = G[v][i];
        if(!used[e.to] && e.cap > 0){
            ll d = find_aug_path_DFS(used, G, e.to, t, min(f, e.cap));
            if(d > 0){
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

ll max_flow_FF(vector<vector<edge>> G, ll s, ll t){
    vector<bool> used(Max_V, false);
    ll flow = 0;
    while(true){
        rep(i, Max_V){
            used[i] = false;
        }
        ll f = find_aug_path_DFS(used, G, s, t, INF);
        if(f == 0) return flow;
        flow += f;
    }
}

vector<ll> compress(vector<ll>& A){
    vector<ll> B = A;
    sort(B.begin(), B.end());
    B.erase(unique(B.begin(), B.end()), B.end());
    vector<ll> res(A.size());
    rep(i, A.size()){
        res[i] = lower_bound(B.begin(), B.end(), A[i]) - B.begin();
    }
    return res;
}

void solve(ll N){
    rep(i, Max_V){
        G[i] = {};
    }
    vector<vector<ll>> dia(N);
    vector<vector<cinq>> data(N - 1);
    rep(i, N - 1){
        ll M; cin >> M;
        rep(j, M){
            ll x, y, c; cin >> x >> y >> c;
            data[i].pb({x, y, c, (ll)dia[i].size(), (ll)dia[i + 1].size()});
            dia[i].pb(x);
            dia[i + 1].pb(y);
        }
    }
    rep(i, N){
        dia[i] = compress(dia[i]);
    }
    rep(i, N - 1){
        vector<bool> added(40);
        rep(j, data[i].size()){
            ll c = get<2>(data[i][j]), k = dia[i][get<3>(data[i][j])], l = dia[i + 1][get<4>(data[i][j])];
            add_edge(k + 60 * i, l + 60 * (i + 1) + 40, 1, c);
            if(!added[l]){
                add_edge(l + 60 * (i + 1) + 40, l + 60 * (i + 1), 1, 0);
                added[l] = true;
            }
        }
    }
    rep(i, N){
        rep(j, dia[i].size() - 1){
            add_edge(j + 60 * i, j + 1 + 60 * i, INF, 0);
        }
    }
    ll g; cin >> g;
    ll s = 0, t = dia[N - 1].size() - 1 + 60 * (N - 1);
    ll ff = max_flow_FF(G, s, t);
    ff = min(ff, g);
    ll cost = min_cost_flow(G, s, t, ff);
    cout << ff << " " << cost << endl;
}


int main(void){
    while(true){
        llin(N);
        if(N == 0) break;
        solve(N);
    }
}
accuracy: 1
time_ms: 110
memory_kb: 5368
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<ll, ll> pr;
typedef pair<pr, ll> prpl;
typedef pair<ll, pr> prlp;
typedef tuple<ll, ll, ll> tri;
typedef tuple<ll, ll, ll, ll, ll> cinq;


const ll INF = (ll)1e18;

#define rep(i, n) for (ll i = 0; i < (ll)(n); i++) // for文の省略
#define all(v) v.begin(), v.end() // 配列範囲の指定の省略。reverse()やsort()に入れる時便利
#define pb push_back

// 変数を宣言して入力を代入する所の省略
template<class... T>
void input(T&... a){
    (cin >> ... >> a);
}
#define llin(...) ll __VA_ARGS__; input(__VA_ARGS__)
void input_vector(vector<ll>& v, ll N){
    rep(i, N){
        cin >> v[i];
    }
}
#define vectin(v, N) vector<ll> v(N); input_vector(v, N)
template<class U>
void input_graph(vector<vector<U>>& G, ll M, bool directed = false, bool weighted = false){
    rep(i, M){
        if(!weighted){
            ll A, B; cin >> A >> B;
            if(!directed){
                G[A].push_back(B);
                G[B].push_back(A);
            } else {
                G[A].push_back(B);
            }
        } else {
            ll A, B, cost; cin >> A >> B >> cost;
            if(!directed){
                G[A].push_back({B, cost});
                G[B].push_back({A, cost});
            } else {
                G[A].push_back({B, cost});
            }
        }
    }
}
#define graphin(G, N, M) vector<vector<ll>> G(N); input_graph(G, M)
#define graphin_directed(G, N, M) vector<vector<ll>> G(N); input_graph(G, M, directed = true)
#define graphin_weighted(G, N, M) vector<vector<pr>> G(N); input_graph(G, M, weighted = true)
#define graphin_directed_weighted(G, N, M) vector<vector<pr>> G(N); input_graph(G, M, directed = true, weighted = true)
template<class U>
void input_edge(vector<U> E, ll M, bool weighted = false){
    rep(i, M){
        if(!weighted){
            ll A, B; cin >> A >> B;
            E[i] = {A, B};
        } else {
            ll A, B, cost; cin >> A >> B >> cost;
            E[i] = {cost, {A, B}};
        }
    }
    if(weighted){
        sort(all(E));
    }
}
#define edgein(E, M) vector<pr> E(M); input_edge(E, M)
#define edgein_weighted(E, M) vector<prlp> E(M); input_edge(E, M, weighted = true)


// デバッグ処理の簡略化。「#define _DEBUG」行をコメントアウトするかどうかで処理を行うかどうか変えられる
void debug_out() { cout << endl; }
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
  cout << H << " ";
  debug_out(T...);
}
#define _DEBUG
#ifdef _DEBUG
#define debug(...) debug_out(__VA_ARGS__)
#else
#define debug(...) 
#endif

#ifndef H_make_vector
#define H_make_vector

/**
 * @brief 多次元 vector の作成
 * @author えびちゃん
 */

#include <cstddef>
#include <type_traits>
#include <vector>

namespace detail {
  template <typename Tp, ll Nb>
  auto make_vector(std::vector<ll>& sizes, Tp const& x) {
    if constexpr (Nb == 1) {
      return vector(sizes[0], x);
    } else {
      ll size = sizes[Nb-1];
      sizes.pop_back();
      return vector(size, make_vector<Tp, Nb-1>(sizes, x));
    }
  }
}  // detail::

template <typename Tp, ll Nb>
auto make_vector(ll const(&sizes)[Nb], Tp const& x = Tp()) {
  vector<ll> s(Nb);
  for (ll i = 0; i < Nb; ++i) s[i] = sizes[Nb-i-1];
  return detail::make_vector<Tp, Nb>(s, x);
}

#endif  /* !defined(H_make_vector) */

template <typename S, typename T>
pair<S, T> operator+(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first + q.first, p.second + q.second);
}
 
template <typename S, typename T>
pair<S, T> operator-(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first - q.first, p.second - q.second);
}

ll Max_V = 6000;

struct edge{
    ll to, cap, cost, rev;
};

vector<vector<edge>> G1(Max_V), G2(Max_V);
vector<ll> h(Max_V);
vector<ll> dist(Max_V);
vector<ll> prevv(Max_V), preve(Max_V);

void add_edge(ll from, ll to, ll cap, ll cost){
    G1[from].pb((edge){to, cap, cost, (ll)G1[to].size()});
    G1[to].pb((edge){from, 0, -cost, (ll)G1[from].size() - 1});
}


ll min_cost_flow(ll s, ll t, ll f){
    ll res = 0;
    rep(i, Max_V){
        h[i] = 0;
    }
    while(f > 0){
        priority_queue<pr, vector<pr>, greater<pr>> que;
        rep(i, Max_V){
            dist[i] = INF;
        }
        dist[s] = 0;
        que.push({0, s});
        while(!que.empty()){
            pr p = que.top(); que.pop();
            ll v = p.second;
            if(dist[v] < p.first) continue;
            rep(i, G2[v].size()){
                edge &e = G2[v][i];
                if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v; preve[e.to] = i;
                    que.push({dist[e.to], e.to});
                }
            }
        }
        if(dist[t] == INF){
            return -1;
        }
        rep(v, Max_V){
            h[v] += dist[v];
        }
        ll d = f;
        for(ll v = t; v != s; v = prevv[v]){
            d = min(d, G2[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d * h[t];
        for(ll v = t; v != s; v = prevv[v]){
            edge &e = G2[prevv[v]][preve[v]];
            e.cap -= d;
            G2[v][e.rev].cap += d;
        }
    }
    return res;
}


vector<bool> used(Max_V, false);

ll find_aug_path_DFS(ll v, ll t, ll f){
    if(v == t) return f;
    used[v] = true;
    rep(i, G1[v].size()){
        edge& e = G1[v][i];
        if(!used[e.to] && e.cap > 0){
            ll d = find_aug_path_DFS(e.to, t, min(f, e.cap));
            if(d > 0){
                e.cap -= d;
                G1[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

ll max_flow_FF(ll s, ll t){
    ll flow = 0;
    while(true){
        rep(i, Max_V){
            used[i] = false;
        }
        ll f = find_aug_path_DFS(s, t, INF);
        if(f == 0) return flow;
        flow += f;
    }
}

vector<ll> compress(vector<ll>& A){
    vector<ll> B = A;
    sort(B.begin(), B.end());
    B.erase(unique(B.begin(), B.end()), B.end());
    vector<ll> res(A.size());
    rep(i, A.size()){
        res[i] = lower_bound(B.begin(), B.end(), A[i]) - B.begin();
    }
    return res;
}

void solve(ll N){
    rep(i, Max_V){
        G1[i] = {}; G2[i] = {};
    }
    vector<vector<ll>> dia(N);
    vector<vector<cinq>> data(N - 1);
    rep(i, N - 1){
        ll M; cin >> M;
        rep(j, M){
            ll x, y, c; cin >> x >> y >> c;
            data[i].pb({x, y, c, (ll)dia[i].size(), (ll)dia[i + 1].size()});
            dia[i].pb(x);
            dia[i + 1].pb(y);
        }
    }
    rep(i, N){
        dia[i] = compress(dia[i]);
    }
    rep(i, N - 1){
        vector<bool> added(40);
        rep(j, data[i].size()){
            ll c = get<2>(data[i][j]), k = dia[i][get<3>(data[i][j])], l = dia[i + 1][get<4>(data[i][j])];
            add_edge(k + 60 * i, l + 60 * (i + 1) + 40, 1, c);
            if(!added[l]){
                add_edge(l + 60 * (i + 1) + 40, l + 60 * (i + 1), 1, 0);
                added[l] = true;
            }
        }
    }
    rep(i, N){
        rep(j, dia[i].size() - 1){
            add_edge(j + 60 * i, j + 1 + 60 * i, INF, 0);
        }
    }
    G2 = G1;
    ll g; cin >> g;
    ll s = 0, t = dia[N - 1].size() - 1 + 60 * (N - 1);
    ll ff = max_flow_FF(s, t);
    ff = min(ff, g);
    ll cost = min_cost_flow(s, t, ff);
    cout << ff << " " << cost << endl;
}


int main(void){
    while(true){
        llin(N);
        if(N == 0) break;
        solve(N);
    }
}
accuracy: 1
time_ms: 110
memory_kb: 5804
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;
using pint = pair<int, int>;
using pll = pair<long long, long long>;

// edge class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct FlowCostEdge {
    // core members
    int rev, from, to;
    FLOWTYPE cap, icap, flow;
    COSTTYPE cost;
    
    // constructor
    FlowCostEdge(int rev, int from, int to, FLOWTYPE cap, COSTTYPE cost)
    : rev(rev), from(from), to(to), cap(cap), icap(cap), flow(0), cost(cost) {}
    void reset() { cap = icap, flow = 0; }
    
    // debug
    friend ostream& operator << (ostream& s, const FlowCostEdge& e) {
        return s << e.from << " -> " << e.to
        << " (" << e.flow << "/" << e.icap << ", " << e.cost << ")";
    }
};

// graph class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct FlowCostGraph {
    // core members
    vector<vector<FlowCostEdge<FLOWTYPE, COSTTYPE>>> list;
    vector<pair<int,int>> pos;  // pos[i] := {vertex, order of list[vertex]} of i-th edge
    
    // constructor
    FlowCostGraph(int n = 0) : list(n) { }
    void init(int n = 0) {
        list.assign(n, FlowCostEdge<FLOWTYPE, COSTTYPE>());
        pos.clear();
    }
    
    // getter
    vector<FlowCostEdge<FLOWTYPE, COSTTYPE>> &operator [] (int i) {
        return list[i];
    }
    const vector<FlowCostEdge<FLOWTYPE, COSTTYPE>> &operator [] (int i) const {
        return list[i];
    }
    size_t size() const {
        return list.size();
    }
    FlowCostEdge<FLOWTYPE, COSTTYPE> &get_rev_edge
    (const FlowCostEdge<FLOWTYPE, COSTTYPE> &e) {
        if (e.from != e.to) return list[e.to][e.rev];
        else return list[e.to][e.rev + 1];
    }
    FlowCostEdge<FLOWTYPE, COSTTYPE> &get_edge(int i) {
        return list[pos[i].first][pos[i].second];
    }
    const FlowCostEdge<FLOWTYPE, COSTTYPE> &get_edge(int i) const {
        return list[pos[i].first][pos[i].second];
    }
    vector<FlowCostEdge<FLOWTYPE, COSTTYPE>> get_edges() const {
        vector<FlowCostEdge<FLOWTYPE, COSTTYPE>> edges;
        for (int i = 0; i < (int)pos.size(); ++i) {
            edges.push_back(get_edge(i));
        }
        return edges;
    }
    
    // change edges
    void reset() {
        for (int i = 0; i < (int)list.size(); ++i) {
            for (FlowCostEdge<FLOWTYPE, COSTTYPE> &e : list[i]) e.reset();
        }
    }
    
    // add_edge
    void add_edge(int from, int to, FLOWTYPE cap, COSTTYPE cost) {
        pos.emplace_back(from, (int)list[from].size());
        list[from].push_back(FlowCostEdge<FLOWTYPE, COSTTYPE>
                             ((int)list[to].size(), from, to, cap, cost));
        list[to].push_back(FlowCostEdge<FLOWTYPE, COSTTYPE>
                           ((int)list[from].size() - 1, to, from, 0, -cost));
    }

    // debug
    friend ostream& operator << (ostream& s, const FlowCostGraph &G) {
        const auto &edges = G.get_edges();
        for (const auto &e : edges) s << e << endl;
        return s;
    }
};

// min-cost max-flow (<= limit_flow), slope ver.
template<class FLOWTYPE, class COSTTYPE> vector<pair<FLOWTYPE, COSTTYPE>>
MinCostFlowSlope(FlowCostGraph<FLOWTYPE, COSTTYPE> &G, int S, int T, FLOWTYPE limit_flow)
{
    // result values
    FLOWTYPE cur_flow = 0;
    COSTTYPE cur_cost = 0, pre_cost = -1;
    vector<pair<FLOWTYPE, COSTTYPE>> res;
    res.emplace_back(cur_flow, cur_cost);
    
    // intermediate values
    vector<COSTTYPE> dual((int)G.size(), 0), dist((int)G.size());
    vector<int> prevv((int)G.size(), -1), preve((int)G.size(), -1);
    
    // dual
    auto dual_step = [&]() -> bool {
        priority_queue<pair<COSTTYPE,int>, vector<pair<COSTTYPE,int>>, greater<pair<COSTTYPE,int>>> que;
        que.push({0, S});
        dist.assign((int)G.size(), numeric_limits<COSTTYPE>::max());
        dist[S] = 0;
        while (!que.empty()) {
            auto [cur_cost, v] = que.top();
            que.pop();
            if (dist[v] < cur_cost) continue;
            for (int i = 0; i < (int)G[v].size(); ++i) {
                const auto &e = G[v][i];
                COSTTYPE new_cost = e.cost + dual[v] - dual[e.to];
                if (e.cap > 0 && dist[e.to] > dist[v] + new_cost) {
                    dist[e.to] = dist[v] + new_cost;
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.push({dist[e.to], e.to});
                }
            }
        }
        if (dist[T] == numeric_limits<COSTTYPE>::max()) return false;
        for (int v = 0; v < (int)G.size(); ++v) {
            if (dist[T] == numeric_limits<COSTTYPE>::max()) continue;
            dual[v] -= dist[T] - dist[v];
        }
        return true;
    };
    
    // primal
    auto primal_step = [&]() -> void {
        FLOWTYPE flow = limit_flow - cur_flow;
        COSTTYPE cost = -dual[S];
        for (int v = T; v != S; v = prevv[v]) {
            flow = min(flow, G[prevv[v]][preve[v]].cap);
        }
        for (int v = T; v != S; v = prevv[v]) {
            FlowCostEdge<FLOWTYPE, COSTTYPE> &e = G[prevv[v]][preve[v]];
            FlowCostEdge<FLOWTYPE, COSTTYPE> &re = G.get_rev_edge(e);
            e.cap -= flow, e.flow += flow;
            re.cap += flow, re.flow -= flow;
        }
        cur_flow += flow;
        cur_cost += flow * cost;
        if (pre_cost == cost) res.pop_back();
        res.emplace_back(cur_flow, cur_cost);
        pre_cost = cur_cost;
    };
    
    // primal-dual
    while (cur_flow < limit_flow) {
        if (!dual_step()) break;
        primal_step();
    }
    return res;
}

// min-cost max-flow, slope ver.
template<class FLOWTYPE, class COSTTYPE> vector<pair<FLOWTYPE, COSTTYPE>>
MinCostFlowSlope(FlowCostGraph<FLOWTYPE, COSTTYPE> &G, int S, int T)
{
    return MinCostFlowSlope(G, S, T, numeric_limits<FLOWTYPE>::max());
}

// min-cost max-flow (<= limit_flow)
template<class FLOWTYPE, class COSTTYPE> pair<FLOWTYPE, COSTTYPE>
MinCostFlow(FlowCostGraph<FLOWTYPE, COSTTYPE> &G, int S, int T, FLOWTYPE limit_flow)
{
    return MinCostFlowSlope(G, S, T, limit_flow).back();
}

// min-cost max-flow (<= limit_flow)
template<class FLOWTYPE, class COSTTYPE> pair<FLOWTYPE, COSTTYPE>
MinCostFlow(FlowCostGraph<FLOWTYPE, COSTTYPE> &G, int S, int T)
{
    return MinCostFlow(G, S, T, numeric_limits<FLOWTYPE>::max());
}

int main() {
    int N, maxF;
    while (cin >> N, N) {
        // 入力
        vector<int> M(N-1);
        vector<vector<int>> X(N-1), Y(N-1), C(N-1);
        
        // (駅, 時刻) → 入って来る側の頂点番号
        map<pint, int> index;
        int vertex_num = 0;
        
        // 各駅の到着時刻と発車時刻
        vector<set<int>> intime(N), outtime(N);
        
        // 入力受け取り
        for (int i = 0; i < N-1; ++i) {
            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) {
                cin >> X[i][j] >> Y[i][j] >> C[i][j];
                outtime[i].insert(X[i][j]);
                intime[i+1].insert(Y[i][j]);
                if (!index.count(pint(i, X[i][j]))) {
                    index[pint(i, X[i][j])] = vertex_num;
                    vertex_num += 2;
                }
                if (!index.count(pint(i+1, Y[i][j]))) {
                    index[pint(i+1, Y[i][j])] = vertex_num;
                    vertex_num += 2;
                }
            }
        }
        cin >> maxF;
        
        // グラフを構築
        int source = vertex_num++, sink = vertex_num++;
        FlowCostGraph<int, int> G(vertex_num);
        const int INF = maxF;  // 理論上流せる上限

        // ソース → 最初の駅
        for (auto t : outtime[0]) {
            G.add_edge(source, index[pint(0, t)] + 1, INF, 0);
        }
        
        // 最後の駅 → シンク
        for (auto t : intime[N-1]) {
            G.add_edge(index[pint(N-1, t)] + 1, sink, INF, 0);
        }
        
        // 各駅の intime (容量 1) を分裂
        for (int i = 0; i < N; ++i) {
            for (auto t : intime[i]) {
                G.add_edge(index[pint(i, t)], index[pint(i, t)] + 1, 1, 0);
            }
        }
        
        // 各駅の intime -> outtime
        for (int i = 0; i < N; ++i) {
            for (auto t1 : intime[i]) {
                for (auto t2 : outtime[i]) {
                    if (t1 <= t2) {
                        G.add_edge(index[pint(i, t1)] + 1, index[pint(i, t2)] + 1, INF, 0);
                    }
                }
            }
        }
        
        // 各電車
        for (int i = 0; i < N-1; ++i) {
            for (int j = 0; j < X[i].size(); ++j) {
                G.add_edge(index[pint(i, X[i][j])] + 1, index[pint(i+1, Y[i][j])], 1, C[i][j]);
            }
        }

        // 最小費用最大流を流す
        auto [max_flow, min_cost] = MinCostFlow(G, source, sink, maxF);
        cout << max_flow << " " << min_cost << endl;
    }
}
accuracy: 1
time_ms: 70
memory_kb: 4096
final_rank: 6

code_snippet: #include <bits/stdc++.h>

using namespace std;

using ll = long long;
using pl = pair<ll, ll>;
#define vl vector<ll>
#define vvl vector<vector<ll>>
#define vvvl vector<vector<vector<ll>>>
#define vm vector<mint>
#define vvm vector<vector<mint>>
#define vvvm vector<vector<vector<mint>>>
#define vd vector<double>
#define vvd vector<vector<double>>
#define vvvd vector<vector<vector<double>>>
#define vp vector<pl>
#define vvp vector<vector<pl>>
#define vs vector<string>
#define vvs vector<vector<string>>

#define _overload3(_1, _2, _3, name, ...) name
#define _rep(i, n) repi(i, 0, n)
#define repi(i, a, b) for (int i = int(a); i < int(b); ++i)
#define rep(...) _overload3(__VA_ARGS__, repi, _rep, )(__VA_ARGS__)
#define all(x) std::begin(x), std::end(x)
#define make_unique(v) v.erase(unique(all(v)), v.end());
#define sum(...) accumulate(all(__VA_ARGS__), 0LL)
#define inf (0x1fffffffffffffff)

template <class T>
istream& operator>>(istream& is, vector<T>& v) {
    for (auto& x : v) {
        is >> x;
    }
    return is;
}

template <class T>
ostream& operator<<(ostream& os, const vector<T>& v) {
    for (int i = 0; i < (int)v.size(); i++) {
        if (i != (int)v.size() - 1) os << v[i] << " ";
        else os << v[i];
    }
    return os;
}

template <typename T, typename... Args>
auto make_v(T x, int arg, Args... args) {
    if constexpr (sizeof...(args) == 0) return vector<T>(arg, x);
    else return vector(arg, make_v<T>(x, args...));
}

template <class T>
auto min(const T& a) {
    return *min_element(all(a));
}

template <class T>
auto max(const T& a) {
    return *max_element(all(a));
}

template <class T>
bool chmin(T& a, const T& b) {
    return a > b ? a = b, true : false;
}

template <class T>
bool chmax(T& a, const T& b) {
    return a < b ? a = b, true : false;
}

struct IoSetup {
    IoSetup() {
        cin.tie(nullptr);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(10);
        cerr << fixed << setprecision(10);
    }
} iosetup;

// struct MinCostFlow {
//     using P = pair<int, int>;
//     const int INF = 1000000000;
//     struct Edge {
//         int to, rev_idx, cap, cost;
//     };
//     int N;
//     vector<vector<Edge>> G;
//     vector<int> h, dist, prev_v, prev_e;

//     MinCostFlow(int N) : N(N), G(N), h(N), dist(N), prev_v(N), prev_e(N) {}

//     void add_edge(int from, int to, int cap, int cost) {
//         G[from].emplace_back((Edge){to, (int)G[to].size(), cap, cost});
//         G[to].emplace_back((Edge){from, (int)G[from].size() - 1, 0, -cost});
//     }

//     int min_cost_flow(int s, int t, int f) {
//         int res = 0;
//         h.assign(N, 0);
//         while(f > 0){
//             priority_queue<P, vector<P>, greater<P>> que;
//             dist.assign(N, INF);
//             dist[s] = 0;
//             que.emplace(0, s);
//             while(!que.empty()){
//                 auto [d, v] = que.top(); que.pop();
//                 if(dist[v] < d) continue;
//                 for(int i = 0; i < G[v].size(); i++){
//                     auto &e = G[v][i];
//                     if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
//                         dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
//                         prev_v[e.to] = v;
//                         prev_e[e.to] = i;
//                         que.emplace(dist[e.to], e.to); 
//                     }
//                 }
//             }
//             if(dist[t] == INF){
//                 return -1;
//             }
//             for(int i = 0; i < N; i++) h[i] = dist[i];

//             int d = f;
//             for(int cur = t; cur != s; cur = prev_v[cur]){
//                 d = min(d, G[prev_v[cur]][prev_e[cur]].cap);
//             }
//             f -= d;
//             res += d * h[t];
//             for (int cur = t; cur != s; cur = prev_v[cur]) {
//                 auto &e = G[prev_v[cur]][prev_e[cur]];
//                 e.cap -= d;
//                 G[cur][e.rev_idx].cap += d;
//             }
//         }
//         return res;
//     }
// };
#line 1 "graph/flow/primal-dual.hpp"
/**
 * @brief Primal Dual(最小費用流)
 * @docs docs/primal-dual.md
 */
template <typename flow_t, typename cost_t>
struct PrimalDual {
    struct edge {
        int to;
        flow_t cap;
        cost_t cost;
        int rev;
        bool isrev;
    };

    vector<vector<edge>> graph;
    vector<cost_t> potential, min_cost;
    vector<int> prevv, preve;
    const cost_t INF;

    PrimalDual(int V) : graph(V), INF(numeric_limits<cost_t>::max()) {}

    void add_edge(int from, int to, flow_t cap, cost_t cost) {
        graph[from].emplace_back(
            (edge){to, cap, cost, (int)graph[to].size(), false});
        graph[to].emplace_back(
            (edge){from, 0, -cost, (int)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);
            que.emplace(0, s);
            min_cost[s] = 0;
            while (!que.empty()) {
                Pi p = que.top();
                que.pop();
                if (min_cost[p.second] < p.first) continue;
                for (int i = 0; i < (int)graph[p.second].size(); i++) {
                    edge& e = graph[p.second][i];
                    cost_t nextCost = min_cost[p.second] + e.cost +
                                      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;
            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;
    }

    void output() {
        for (int i = 0; i < graph.size(); i++) {
            for (auto& e : graph[i]) {
                if (e.isrev) continue;
                auto& rev_e = graph[e.to][e.rev];
                cout << i << "->" << e.to << " (flow: " << rev_e.cap << "/"
                     << rev_e.cap + e.cap << ")" << endl;
            }
        }
    }
};

using MinCostFlow = PrimalDual<int, int>;

int main(){
    // MinCostFlow mincostflow(4);
    // mincostflow.add_edge(0, 1, 2, 1);
    // mincostflow.add_edge(0, 2, 3, 1);
    // mincostflow.add_edge(1, 2, 1, 1);
    // mincostflow.add_edge(1, 3, 1, 1);
    // mincostflow.add_edge(2, 3, 4, 1);
    // cout << mincostflow.min_cost_flow(0, 3, 4) << endl;
    while(true){
        ll n;
        cin >> n;
        if(n == 0) break;
        vl m(n - 1);
        vvl x(n - 1), y(n - 1), c(n - 1);
        ll s = 1;
        vector<map<int, int>> vin(n - 1), vout(n - 1);
        rep(i, n - 1){
            cin >> m[i];
            x[i].resize(m[i]);
            y[i].resize(m[i]);
            c[i].resize(m[i]);
            rep(j, m[i]){
                cin >> x[i][j] >> y[i][j] >> c[i][j];
                if(vin[i][y[i][j]] == 0){
                    vin[i][y[i][j]] = s; s++;
                    vout[i][y[i][j]] = s; s++;
                }
            }
        }
        s += 1;
        int g;
        cin >> g;
        ll ans_g = 0, ans_c = 0;
        for(int cl = g; cl >= 1; --cl){
            MinCostFlow mcf(s);
            ll s1 = 0, sn = s - 1;
            rep(i, m[0]) {
                mcf.add_edge(s1, vin[0][y[0][i]], 1, 0);
            }
            rep(i, n - 1) {
                set<ll> used;
                rep(j, m[i]){
                    if(used.find(y[i][j]) == used.end()){
                        mcf.add_edge(vin[i][y[i][j]], vout[i][y[i][j]], 1, 0);
                        used.insert(y[i][j]);
                    }
                }
                if (i == n - 2) {
                    rep(j, m[i]) { mcf.add_edge(vout[i][y[i][j]], sn, 1, c[i][j]); }
                    continue;
                }
                rep(j, m[i]) {
                    rep(k, m[i + 1]) {
                        if (y[i][j] <= x[i + 1][k]) {
                            mcf.add_edge(vout[i][y[i][j]], vin[i + 1][y[i + 1][k]], 1, c[i][j]);
                        }
                    }
                }
            }
            int res = mcf.min_cost_flow(s1, sn, cl);
            if (res != -1) {
                ans_g = cl;
                ans_c = res;
                break;
            }
        }
        cout << ans_g << " " << ans_c << endl;
    }
}
accuracy: 1
time_ms: 420
memory_kb: 6356
final_rank: 14

code_snippet: //
// 最小費用最大流 (正辺のみ)
//
// verified
//   AOJ 1088 School Excursion
//     https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1088
//


#include <bits/stdc++.h>
using namespace std;
using pint = pair<int, int>;
using pll = pair<long long, long long>;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }

#define REP(i, n) for (long long i = 0; i < (long long)(n); ++i)
#define REP2(i, a, b) for (long long i = a; i < (long long)(b); ++i)
#define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, deque<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for(auto it : P) { s << "<" << it << "> "; } return s; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for(auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; }


// edge class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct Edge {
    int rev, from, to, id;
    FLOWTYPE cap, icap;
    COSTTYPE cost;
    Edge(int r, int f, int t, FLOWTYPE ca, COSTTYPE co, int id = -1) :
        rev(r), from(f), to(t), cap(ca), icap(ca), cost(co), id(id) {}
    friend ostream& operator << (ostream& s, const Edge& E) {
        if (E.cap > 0)
            return s << E.from << "->" << E.to <<
                '(' << E.cap << ',' << E.cost << ')';
        else return s;
    }
};

// graph class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct Graph {
    vector<vector<Edge<FLOWTYPE, COSTTYPE> > > list;
    
    Graph(int n = 0) : list(n) { }
    void init(int n = 0) { list.clear(); list.resize(n); }
    void reset() { for (int i = 0; i < (int)list.size(); ++i) for (int j = 0; j < list[i].size(); ++j) list[i][j].cap = list[i][j].icap; }
    inline vector<Edge<FLOWTYPE, COSTTYPE> >& operator [] (int i) { return list[i]; }
    inline const size_t size() const { return list.size(); }
    
    inline Edge<FLOWTYPE, COSTTYPE> &redge(const Edge<FLOWTYPE, COSTTYPE> &e) {
        if (e.from != e.to) return list[e.to][e.rev];
        else return list[e.to][e.rev + 1];
    }
    
    void addedge(int from, int to, FLOWTYPE cap, COSTTYPE cost, int id = -1) {
        list[from].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[to].size(), from, to, cap, cost, id));
        list[to].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[from].size() - 1, to, from, 0, -cost));
    }
    
    void add_undirected_edge(int from, int to, FLOWTYPE cap, COSTTYPE cost, int id = -1) {
        list[from].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[to].size(), from, to, cap, cost, id));
        list[to].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[from].size() - 1, to, from, cap, cost, id));
    }

    friend ostream& operator << (ostream& s, const Graph& G) {
        s << endl;
        for (int i = 0; i < G.size(); ++i) {
            s << i << ":";
            for (auto e : G.list[i]) s << " " << e;
            s << endl;
        }
        return s;
    }
};

// min-cost max-flow (by primal-dual), maxf is the upper-limit of flow
template<class FLOWTYPE, class COSTTYPE> pair<FLOWTYPE, COSTTYPE> MinCostMaxFlow
 (Graph<FLOWTYPE, COSTTYPE> &G, int s, int t, FLOWTYPE maxf) {
    int n = (int)G.size();
    vector<COSTTYPE> pot(n, 0), dist(n, -1);
    vector<int> prevv(n), preve(n);
    FLOWTYPE f = maxf;
    COSTTYPE res = 0;
    while (f > 0) {
        priority_queue<pair<COSTTYPE,int>, vector<pair<COSTTYPE,int> >, greater<pair<COSTTYPE,int> > > que;
        dist.assign(n, -1);
        dist[s] = 0;
        que.push(make_pair(0,s));
        while(!que.empty()) {
            pair<COSTTYPE,int> p = que.top();
            que.pop();
            int v = p.second;
            if (dist[v] < p.first) continue;
            for (int i = 0; i < G[v].size(); ++i) {
                auto e = G[v][i];
                if (e.cap > 0 && (dist[e.to] < 0 || dist[e.to] > dist[v] + e.cost + pot[v] - pot[e.to])) {
                    dist[e.to] = dist[v] + e.cost + pot[v] - pot[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.push(make_pair(dist[e.to], e.to));
                }
            }
        }
        if (dist[t] < 0) return {maxf - f, res};
        for (int v = 0; v < n; ++v) pot[v] += dist[v];
        FLOWTYPE d = f;
        for (int v = t; v != s; v = prevv[v]) {
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += pot[t] * d;
        for (int v = t; v != s; v = prevv[v]) {
            Edge<FLOWTYPE,COSTTYPE> &e = G[prevv[v]][preve[v]];
            Edge<FLOWTYPE,COSTTYPE> &re = G.redge(e);
            e.cap -= d;
            re.cap += d;
        }
    }
    return {maxf, res};
}

int main() {
    int N, maxF;
    while (cin >> N, N) {
        // 入力
        vector<int> M(N-1);
        vector<vector<int>> X(N-1), Y(N-1), C(N-1);
        
        // (駅, 時刻) → 入って来る側の頂点番号
        map<pint, int> index;
        int vertex_num = 0;
        
        // 各駅の到着時刻と発車時刻
        vector<set<int>> intime(N), outtime(N);
        
        // 入力受け取り
        for (int i = 0; i < N-1; ++i) {
            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) {
                cin >> X[i][j] >> Y[i][j] >> C[i][j];
                outtime[i].insert(X[i][j]);
                intime[i+1].insert(Y[i][j]);
                if (!index.count(pint(i, X[i][j]))) {
                    index[pint(i, X[i][j])] = vertex_num;
                    vertex_num += 2;
                }
                if (!index.count(pint(i+1, Y[i][j]))) {
                    index[pint(i+1, Y[i][j])] = vertex_num;
                    vertex_num += 2;
                }
            }
        }
        cin >> maxF;
        
        // グラフを構築
        int source = vertex_num++, sink = vertex_num++;
        Graph<int, int> G(vertex_num);
        const int INF = maxF;  // 理論上流せる上限

        // ソース → 最初の駅
        for (auto t : outtime[0]) {
            G.addedge(source, index[pint(0, t)] + 1, INF, 0);
        }
        
        // 最後の駅 → シンク
        for (auto t : intime[N-1]) {
            G.addedge(index[pint(N-1, t)] + 1, sink, INF, 0);
        }
        
        // 各駅の intime (容量 1) を分裂
        for (int i = 0; i < N; ++i) {
            for (auto t : intime[i]) {
                G.addedge(index[pint(i, t)], index[pint(i, t)] + 1, 1, 0);
            }
        }
        
        // 各駅の intime -> outtime
        for (int i = 0; i < N; ++i) {
            for (auto t1 : intime[i]) {
                for (auto t2 : outtime[i]) {
                    if (t1 <= t2) {
                        G.addedge(index[pint(i, t1)] + 1, index[pint(i, t2)] + 1, INF, 0);
                    }
                }
            }
        }
        
        // 各電車
        for (int i = 0; i < N-1; ++i) {
            for (int j = 0; j < X[i].size(); ++j) {
                G.addedge(index[pint(i, X[i][j])] + 1, index[pint(i+1, Y[i][j])], 1, C[i][j]);
            }
        }

        // 最小費用最大流を流す
        auto res = MinCostMaxFlow(G, source, sink, maxF);
        cout << res.first << " " << res.second << endl;
    }
}
accuracy: 1
time_ms: 70
memory_kb: 4216
final_rank: 7

code_snippet: //
// 最小費用最大流 (正辺のみ)
//
// verified
//   AOJ 1088 School Excursion
//     https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1088
//


#include <bits/stdc++.h>
using namespace std;
using pint = pair<int, int>;
using pll = pair<long long, long long>;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }

#define REP(i, n) for (long long i = 0; i < (long long)(n); ++i)
#define REP2(i, a, b) for (long long i = a; i < (long long)(b); ++i)
#define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, deque<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for(auto it : P) { s << "<" << it << "> "; } return s; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for(auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; }


// edge class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct Edge {
    int rev, from, to, id;
    FLOWTYPE cap, icap;
    COSTTYPE cost;
    Edge(int r, int f, int t, FLOWTYPE ca, COSTTYPE co, int id = -1) :
        rev(r), from(f), to(t), cap(ca), icap(ca), cost(co), id(id) {}
    friend ostream& operator << (ostream& s, const Edge& E) {
        if (E.cap > 0)
            return s << E.from << "->" << E.to <<
                '(' << E.cap << ',' << E.cost << ')';
        else return s;
    }
};

// graph class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct Graph {
    vector<vector<Edge<FLOWTYPE, COSTTYPE> > > list;
    
    Graph(int n = 0) : list(n) { }
    void init(int n = 0) { list.clear(); list.resize(n); }
    void reset() { for (int i = 0; i < (int)list.size(); ++i) for (int j = 0; j < list[i].size(); ++j) list[i][j].cap = list[i][j].icap; }
    inline vector<Edge<FLOWTYPE, COSTTYPE> >& operator [] (int i) { return list[i]; }
    inline const size_t size() const { return list.size(); }
    
    inline Edge<FLOWTYPE, COSTTYPE> &redge(const Edge<FLOWTYPE, COSTTYPE> &e) {
        if (e.from != e.to) return list[e.to][e.rev];
        else return list[e.to][e.rev + 1];
    }
    
    void addedge(int from, int to, FLOWTYPE cap, COSTTYPE cost, int id = -1) {
        list[from].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[to].size(), from, to, cap, cost, id));
        list[to].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[from].size() - 1, to, from, 0, -cost));
    }
    
    void add_undirected_edge(int from, int to, FLOWTYPE cap, COSTTYPE cost, int id = -1) {
        list[from].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[to].size(), from, to, cap, cost, id));
        list[to].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[from].size() - 1, to, from, cap, cost, id));
    }

    friend ostream& operator << (ostream& s, const Graph& G) {
        s << endl;
        for (int i = 0; i < G.size(); ++i) {
            s << i << ":";
            for (auto e : G.list[i]) s << " " << e;
            s << endl;
        }
        return s;
    }
};

// min-cost max-flow (by primal-dual), maxf is the upper-limit of flow
template<class FLOWTYPE, class COSTTYPE> pair<FLOWTYPE, COSTTYPE> MinCostMaxFlow
 (Graph<FLOWTYPE, COSTTYPE> &G, int s, int t, FLOWTYPE maxf) {
    int n = (int)G.size();
    vector<COSTTYPE> pot(n, 0), dist(n, -1);
    vector<int> prevv(n), preve(n);
    FLOWTYPE f = maxf;
    COSTTYPE res = 0;
    while (f > 0) {
        priority_queue<pair<COSTTYPE,int>, vector<pair<COSTTYPE,int> >, greater<pair<COSTTYPE,int> > > que;
        dist.assign(n, -1);
        dist[s] = 0;
        que.push(make_pair(0,s));
        while(!que.empty()) {
            pair<COSTTYPE,int> p = que.top();
            que.pop();
            int v = p.second;
            if (dist[v] < p.first) continue;
            for (int i = 0; i < G[v].size(); ++i) {
                auto e = G[v][i];
                if (e.cap > 0 && (dist[e.to] < 0 || dist[e.to] > dist[v] + e.cost + pot[v] - pot[e.to])) {
                    dist[e.to] = dist[v] + e.cost + pot[v] - pot[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.push(make_pair(dist[e.to], e.to));
                }
            }
        }
        if (dist[t] < 0) return {maxf - f, res};
        for (int v = 0; v < n; ++v) pot[v] += dist[v];
        FLOWTYPE d = f;
        for (int v = t; v != s; v = prevv[v]) {
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += pot[t] * d;
        for (int v = t; v != s; v = prevv[v]) {
            Edge<FLOWTYPE,COSTTYPE> &e = G[prevv[v]][preve[v]];
            Edge<FLOWTYPE,COSTTYPE> &re = G.redge(e);
            e.cap -= d;
            re.cap += d;
        }
    }
    return {maxf, res};
}

int main() {
    int N, maxF;
    while (cin >> N, N) {
        // 入力
        vector<int> M(N-1);
        vector<vector<int>> X(N-1), Y(N-1), C(N-1);
        
        // (駅, 時刻) → 入って来る側の頂点番号
        map<pint, int> index;
        int vertex_num = 0;
        
        vector<pint> verinfo;
        
        // 各駅の到着時刻と発車時刻
        vector<set<int>> intime(N), outtime(N);
        
        // 入力受け取り
        for (int i = 0; i < N-1; ++i) {
            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) {
                cin >> X[i][j] >> Y[i][j] >> C[i][j];
                outtime[i].insert(X[i][j]);
                intime[i+1].insert(Y[i][j]);
                if (!index.count(pint(i, X[i][j]))) {
                    index[pint(i, X[i][j])] = vertex_num;
                    vertex_num += 2;
                    
                    verinfo.push_back(pint(i, X[i][j]));
                    verinfo.push_back(pint(i, X[i][j]));
                }
                if (!index.count(pint(i+1, Y[i][j]))) {
                    index[pint(i+1, Y[i][j])] = vertex_num;
                    vertex_num += 2;
                    
                    verinfo.push_back(pint(i+1, Y[i][j]));
                    verinfo.push_back(pint(i+1, Y[i][j]));
                }
            }
        }
        cin >> maxF;
        
        // グラフを構築
        int source = vertex_num++, sink = vertex_num++;
        Graph<int, int> G(vertex_num);
        const int INF = maxF;  // 理論上流せる上限
        
        verinfo.emplace_back(-1, -1);verinfo.emplace_back(-1, -1);
        
        //COUT(verinfo); COUT(index); COUT(intime); COUT(outtime);
        
        // ソース → 最初の駅
        for (auto t : outtime[0]) {
            G.addedge(source, index[pint(0, t)] + 1, INF, 0);
        }
        // 最後の駅 → シンク
        for (auto t : intime[N-1]) {
            G.addedge(index[pint(N-1, t)] + 1, sink, INF, 0);
        }
        
        // 各駅の intime (容量 1) を分裂
        for (int i = 0; i < N; ++i) {
            for (auto t : intime[i]) {
                G.addedge(index[pint(i, t)], index[pint(i, t)] + 1, 1, 0);
            }
        }
        
        // 各駅の intime -> outtime
        for (int i = 0; i < N; ++i) {
            for (auto t1 : intime[i]) {
                for (auto t2 : outtime[i]) {
                    if (t1 <= t2) {
                        G.addedge(index[pint(i, t1)] + 1, index[pint(i, t2)] + 1, INF, 0);
                    }
                }
            }
        }
        
        // 各電車
        for (int i = 0; i < N-1; ++i) {
            for (int j = 0; j < X[i].size(); ++j) {
                G.addedge(index[pint(i, X[i][j])] + 1, index[pint(i+1, Y[i][j])], 1, C[i][j]);
            }
        }
        
        
        /*
        for (int v = 0; v < vertex_num; ++v) {
            for (auto e : G[v]) {
                if (e.cap > 0)
                cout << verinfo[v] << " -> " << verinfo[e.to] << " (" << e.icap << ", " << e.cost << ")" << endl;
            }
        }
        */
        
        
        // 最小費用最大流を流す
        auto res = MinCostMaxFlow(G, source, sink, maxF);
        cout << res.first << " " << res.second << endl;
    }
}
accuracy: 1
time_ms: 70
memory_kb: 4300
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
#define drep(i, cc, n) for (ll i = (cc); i <= (n); ++i)
#define rep(i, n) drep(i, 0, n - 1)
#define all(a) (a).begin(), (a).end()
#define pb push_back
#define fi first
#define se second

const ll MOD = 1000000007;
const ll MOD2 = 998244353;
const ll INF = 1LL << 60;
const ll MAX_N = 2e5;

struct MinCostFlow{
    struct Edge{
        ll to;
        ll cap;
        ll cost;
        ll rev;
    };

    vector<vector<Edge>> G;
    ll N;
    vector<ll> h; // ポテンシャル
    vector<ll> dist; // 最短距離
    vector<ll> prevv; // 直前の頂点
    vector<ll> preve; // 直前の辺

    MinCostFlow(vector<vector<tuple<ll, ll, ll>>> G_){
        N = (ll)G_.size();
        G.resize(N);
        h.resize(N, 0);
        dist.resize(N, 0);
        prevv.resize(N, 0);
        preve.resize(N, 0);

        rep(from, N){
            for(tuple<ll, ll, ll> tmp : G_[from]){
                ll to = get<0>(tmp);
                ll cap = get<1>(tmp);
                ll cost = get<2>(tmp);
                G[from].pb((Edge){to, cap, cost, (ll)G[to].size()});
                G[to].pb((Edge){from, 0, -cost, (ll)G[from].size()-1});
            }
        }
    }

    ll min_cost_flow(ll s, ll t, ll f){
        vector<vector<Edge>> H(N);
        rep(from, N) for(Edge e : G[from]) H[from].pb(e);
        rep(i, N) h[i] = 0;

        ll res = 0;
        while(f > 0){
            //ダイクストラ法を用いてhを更新
            priority_queue<pll, vector<pll>, greater<pll>> PQ;
            rep(i, N) dist[i] = INF;
            dist[s] = 0;
            PQ.push({0, s});
            while(!PQ.empty()){
                pll p = PQ.top();
                PQ.pop();
                ll v = p.se;
                if(dist[v] < p.fi) continue;

                for(ll i=0; i<(ll)H[v].size(); i++){
                    Edge &e = H[v][i];
                    if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                        dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        PQ.push({dist[e.to], e.to});
                    }
                }
            }
            if(dist[t] == INF){
                //これ以上流せない
                return -1;
            }
            for(ll v=0; v<N; v++) h[v] += dist[v];

            // s-t間最短経路に沿って目一杯流す
            ll d = f;
            for(ll v=t; v!=s; v=prevv[v]){
                d = min(d, H[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d*h[t];
            for(ll v=t; v!=s; v=prevv[v]){
                Edge &e = H[prevv[v]][preve[v]];
                e.cap -= d;
                H[v][e.rev].cap += d;
            }
        }

        return res;
    }
};

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    
    vector<pll> ans;
    while(true){
        ll n;
        cin >> n;

        if(n == 0) break;

        vector<vector<tuple<ll, ll, ll>>> train(n+1);
        for(ll i=1; i<=n-1; i++){
            ll m;
            cin >> m;

            rep(j, m){
                ll x, y, c;
                cin >> x >> y >> c;
                train[i].pb({x, y, c});
            }
        }

        ll g;
        cin >> g;

        //頂点番号の割り当て
        vector<map<ll, ll>> in_t(n+1), out_t(n+1);
        ll now = 0;
        in_t[1][0]=now++;
        for(ll i=1; i<=n-1; i++){
            for(tuple<ll, ll, ll> tmp : train[i]){
                ll x = get<0>(tmp);
                ll y = get<1>(tmp);

                for(auto itr=in_t[i].begin(); itr!=in_t[i].end(); itr++){
                    if(out_t[i].find((*itr).fi) == out_t[i].end()) out_t[i][(*itr).fi] = now++;
                }
                if(out_t[i].find(x) == out_t[i].end()) out_t[i][x] = now++;
                if(in_t[i+1].find(y) == in_t[i+1].end()) in_t[i+1][y] = now++;

                if(i == n-1){
                    if(out_t[i+1].find(y) == out_t[i+1].end()) out_t[i+1][y] = now++;
                }
            }
        }

        //グラフの構築
        vector<vector<tuple<ll, ll, ll>>> G(now);
        ll s = 0;
        G[s].pb({1, g, 0});
        for(ll i=1; i<=n-1; i++){
            if(i != 1){
                for(auto itr=in_t[i].begin(); itr!=in_t[i].end(); itr++){
                    ll time = (*itr).fi;
                    ll u = (*itr).se;
                    ll v = out_t[i][time];
                    G[u].pb({v, 1, 0});
                }
            }

            for(auto itr=out_t[i].begin(); itr!=out_t[i].end(); itr++){
                auto nxt_itr = itr;
                nxt_itr++;
                if(nxt_itr==out_t[i].end()) break;
                ll u = (*itr).se;
                ll v = (*nxt_itr).se;
                if(u == 0) cout << "error : " << i << " " << (*itr).fi << " " << (*itr).se << endl;
                G[u].pb({v, INF, 0});
            }

            for(tuple<ll, ll, ll> tmp : train[i]){
                ll x = get<0>(tmp);
                ll y = get<1>(tmp);
                ll c = get<2>(tmp);

                ll u = out_t[i][x];
                ll v = in_t[i+1][y];
                G[u].pb({v, 1, c});
            }
        }

        for(auto itr=in_t[n].begin(); itr!=in_t[n].end(); itr++){
            ll time = (*itr).fi;
            ll u = (*itr).se;
            ll v = out_t[n][time];
            G[u].pb({v, 1, 0});
        }

        ll t=((*out_t[n].begin()).se); //終点
        for(auto itr=out_t[n].begin(); itr!=out_t[n].end(); itr++){
            auto nxt_itr = itr;
            nxt_itr++;
            if(nxt_itr==out_t[n].end()) break;
            ll u = (*itr).se;
            ll v = (*nxt_itr).se;
            G[u].pb({v, INF, 0});
            t = v;
        }

        MinCostFlow MCF(G);

        ll lw = 0;
        ll hi = g+1;
        ll mid;
        while(hi - lw > 1){
            mid = (lw + hi)/2;

            ll mcf = MCF.min_cost_flow(s, t, mid);
            if(mcf==-1) hi = mid;
            else lw = mid;
        }

        ans.pb({lw, MCF.min_cost_flow(s, t, lw)});
    }

    rep(i, (ll)ans.size()) cout << ans[i].fi << " " << ans[i].se << endl;
}
accuracy: 1
time_ms: 280
memory_kb: 5192
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
#define drep(i, cc, n) for (ll i = (cc); i <= (n); ++i)
#define rep(i, n) drep(i, 0, n - 1)
#define all(a) (a).begin(), (a).end()
#define pb push_back
#define fi first
#define se second

const ll MOD = 1000000007;
const ll MOD2 = 998244353;
const ll INF = 1LL << 60;
const ll MAX_N = 2e5;

struct MinCostFlow{
    struct Edge{
        ll to;
        ll cap;
        ll cost;
        ll rev;
    };

    vector<vector<Edge>> G;
    ll N;
    vector<ll> h; // ポテンシャル
    vector<ll> dist; // 最短距離
    vector<ll> prevv; // 直前の頂点
    vector<ll> preve; // 直前の辺

    MinCostFlow(vector<vector<tuple<ll, ll, ll>>> G_){
        N = (ll)G_.size();
        G.resize(N);
        h.resize(N, 0);
        dist.resize(N, 0);
        prevv.resize(N, 0);
        preve.resize(N, 0);

        rep(from, N){
            for(tuple<ll, ll, ll> tmp : G_[from]){
                ll to = get<0>(tmp);
                ll cap = get<1>(tmp);
                ll cost = get<2>(tmp);
                G[from].pb((Edge){to, cap, cost, (ll)G[to].size()});
                G[to].pb((Edge){from, 0, -cost, (ll)G[from].size()-1});
            }
        }
    }

    ll min_cost_flow(ll s, ll t, ll f){
        vector<vector<Edge>> H(N);
        rep(from, N) for(Edge e : G[from]) H[from].pb(e);
        rep(i, N) h[i] = 0;

        ll res = 0;
        while(f > 0){
            //ダイクストラ法を用いてhを更新
            priority_queue<pll, vector<pll>, greater<pll>> PQ;
            rep(i, N) dist[i] = INF;
            dist[s] = 0;
            PQ.push({0, s});
            while(!PQ.empty()){
                pll p = PQ.top();
                PQ.pop();
                ll v = p.se;
                if(dist[v] < p.fi) continue;

                for(ll i=0; i<(ll)H[v].size(); i++){
                    Edge &e = H[v][i];
                    if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                        dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        PQ.push({dist[e.to], e.to});
                    }
                }
            }
            if(dist[t] == INF){
                //これ以上流せない
                return -1;
            }
            for(ll v=0; v<N; v++) h[v] += dist[v];

            // s-t間最短経路に沿って目一杯流す
            ll d = f;
            for(ll v=t; v!=s; v=prevv[v]){
                d = min(d, H[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d*h[t];
            for(ll v=t; v!=s; v=prevv[v]){
                Edge &e = H[prevv[v]][preve[v]];
                e.cap -= d;
                H[v][e.rev].cap += d;
            }
        }

        return res;
    }
};

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    
    vector<pll> ans;
    while(true){
        ll n;
        cin >> n;

        if(n == 0) break;

        vector<vector<tuple<ll, ll, ll>>> train(n+1);
        for(ll i=1; i<=n-1; i++){
            ll m;
            cin >> m;

            rep(j, m){
                ll x, y, c;
                cin >> x >> y >> c;
                train[i].pb({x, y, c});
            }
        }

        ll g;
        cin >> g;

        //頂点番号の割り当て
        vector<map<ll, ll>> in_t(n+1), out_t(n+1);
        ll now = 0;
        in_t[1][0]=now++;
        for(ll i=1; i<=n-1; i++){
            for(tuple<ll, ll, ll> tmp : train[i]){
                ll x = get<0>(tmp);
                ll y = get<1>(tmp);

                for(auto itr=in_t[i].begin(); itr!=in_t[i].end(); itr++){
                    if(out_t[i].find((*itr).fi) == out_t[i].end()) out_t[i][(*itr).fi] = now++;
                }
                if(out_t[i].find(x) == out_t[i].end()) out_t[i][x] = now++;
                if(in_t[i+1].find(y) == in_t[i+1].end()) in_t[i+1][y] = now++;

                if(i == n-1){
                    if(out_t[i+1].find(y) == out_t[i+1].end()) out_t[i+1][y] = now++;
                }
            }
        }

        //グラフの構築
        vector<vector<tuple<ll, ll, ll>>> G(now);
        ll s = 0;
        G[s].pb({1, g, 0});
        for(ll i=1; i<=n-1; i++){
            if(i != 1){
                for(auto itr=in_t[i].begin(); itr!=in_t[i].end(); itr++){
                    ll time = (*itr).fi;
                    ll u = (*itr).se;
                    ll v = out_t[i][time];
                    G[u].pb({v, 1, 0});
                }
            }

            for(auto itr=out_t[i].begin(); itr!=out_t[i].end(); itr++){
                auto nxt_itr = itr;
                nxt_itr++;
                if(nxt_itr==out_t[i].end()) break;
                ll u = (*itr).se;
                ll v = (*nxt_itr).se;
                G[u].pb({v, INF, 0});
            }

            for(tuple<ll, ll, ll> tmp : train[i]){
                ll x = get<0>(tmp);
                ll y = get<1>(tmp);
                ll c = get<2>(tmp);

                ll u = out_t[i][x];
                ll v = in_t[i+1][y];
                G[u].pb({v, 1, c});
            }
        }

        for(auto itr=in_t[n].begin(); itr!=in_t[n].end(); itr++){
            ll time = (*itr).fi;
            ll u = (*itr).se;
            ll v = out_t[n][time];
            G[u].pb({v, 1, 0});
        }

        ll t=0; //終点
        for(auto itr=out_t[n].begin(); itr!=out_t[n].end(); itr++){
            auto nxt_itr = itr;
            nxt_itr++;
            if(nxt_itr==out_t[n].end()) break;
            ll u = (*itr).se;
            ll v = (*nxt_itr).se;
            G[u].pb({v, INF, 0});
            t = v;
        }

        MinCostFlow MCF(G);

        ll lw = 0;
        ll hi = g+1;
        ll mid;
        while(hi - lw > 1){
            mid = (lw + hi)/2;

            ll mcf = MCF.min_cost_flow(s, t, mid);
            if(mcf==-1) hi = mid;
            else lw = mid;
        }

        ans.pb({lw, MCF.min_cost_flow(s, t, lw)});
    }

    rep(i, (ll)ans.size()) cout << ans[i].fi << " " << ans[i].se << endl;
}
accuracy: 0.6666666666666666
time_ms: 290
memory_kb: 5428
final_rank: 18

code_snippet: #ifndef HIDDEN_IN_VS // 折りたたみ用

// 警告の抑制
#define _CRT_SECURE_NO_WARNINGS

// ライブラリの読み込み
#include <bits/stdc++.h>
using namespace std;

// 型名の短縮
using ll = long long; // -2^63 ～ 2^63 = 9 * 10^18（int は -2^31 ～ 2^31 = 2 * 10^9）
using pii = pair<int, int>;	using pll = pair<ll, ll>;	using pil = pair<int, ll>;	using pli = pair<ll, int>;
using vi = vector<int>;		using vvi = vector<vi>;		using vvvi = vector<vvi>;
using vl = vector<ll>;		using vvl = vector<vl>;		using vvvl = vector<vvl>;
using vb = vector<bool>;	using vvb = vector<vb>;		using vvvb = vector<vvb>;
using vc = vector<char>;	using vvc = vector<vc>;		using vvvc = vector<vvc>;
using vd = vector<double>;	using vvd = vector<vd>;		using vvvd = vector<vvd>;
template <class T> using priority_queue_rev = priority_queue<T, vector<T>, greater<T>>;
using Graph = vvi;

// 定数の定義
const double PI = acos(-1);
const vi DX = { 1, 0, -1, 0 }; // 4 近傍（下，右，上，左）
const vi DY = { 0, 1, 0, -1 };
int INF = 1001001001; ll INFL = 4004004004004004004LL;
double EPS = 1e-12;

// 入出力高速化
struct fast_io { fast_io() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(18); } } fastIOtmp;

// 汎用マクロの定義
#define all(a) (a).begin(), (a).end()
#define sz(x) ((int)(x).size())
#define lbpos(a, x) (int)distance((a).begin(), std::lower_bound(all(a), x))
#define ubpos(a, x) (int)distance((a).begin(), std::upper_bound(all(a), x))
#define Yes(b) {cout << ((b) ? "Yes\n" : "No\n");}
#define rep(i, n) for(int i = 0, i##_len = int(n); i < i##_len; ++i) // 0 から n-1 まで昇順
#define repi(i, s, t) for(int i = int(s), i##_end = int(t); i <= i##_end; ++i) // s から t まで昇順
#define repir(i, s, t) for(int i = int(s), i##_end = int(t); i >= i##_end; --i) // s から t まで降順
#define repe(v, a) for(const auto& v : (a)) // a の全要素（変更不可能）
#define repea(v, a) for(auto& v : (a)) // a の全要素（変更可能）
#define repb(set, d) for(int set = 0; set < (1 << int(d)); ++set) // d ビット全探索（昇順）
#define repp(a) sort(all(a)); for(bool a##_perm = true; a##_perm; a##_perm = next_permutation(all(a))) // a の順列全て（昇順）
#define smod(n, m) ((((n) % (m)) + (m)) % (m)) // 非負mod
#define uniq(a) {sort(all(a)); (a).erase(unique(all(a)), (a).end());} // 重複除去
#define EXIT(a) {cout << (a) << endl; exit(0);} // 強制終了

// 汎用関数の定義
template <class T> inline ll pow(T n, int k) { ll v = 1; rep(i, k) v *= n; return v; }
template <class T> inline bool chmax(T& M, const T& x) { if (M < x) { M = x; return true; } return false; } // 最大値を更新（更新されたら true を返す）
template <class T> inline bool chmin(T& m, const T& x) { if (m > x) { m = x; return true; } return false; } // 最小値を更新（更新されたら true を返す）

// 演算子オーバーロード
template <class T, class U> inline istream& operator>>(istream& is, pair<T, U>& p) { is >> p.first >> p.second; return is; }
template <class T> inline istream& operator>>(istream& is, vector<T>& v) { repea(x, v) is >> x; return is; }
template <class T> inline vector<T>& operator--(vector<T>& v) { repea(x, v) --x; return v; }
template <class T> inline vector<T>& operator++(vector<T>& v) { repea(x, v) ++x; return v; }

// 手元環境（Visual Studio）
#ifdef _MSC_VER
#include "local.hpp"
// 提出用（gcc）
#else
inline int popcount(int n) { return __builtin_popcount(n); }
inline int popcount(ll n) { return __builtin_popcountll(n); }
inline int lsb(int n) { return n != 0 ? __builtin_ctz(n) : -1; }
inline int lsb(ll n) { return n != 0 ? __builtin_ctzll(n) : -1; }
inline int msb(int n) { return n != 0 ? (31 - __builtin_clz(n)) : -1; }
inline int msb(ll n) { return n != 0 ? (63 - __builtin_clzll(n)) : -1; }
#define gcd __gcd
#define dump(...)
#define dumpel(v)
#define dump_list(v)
#define input_from_file(f)
#define output_to_file(f)
#define Assert(b) { if (!(b)) while (1) cout << "OLE"; }
#endif

#endif // 折りたたみ用


////--------------AtCoder 専用--------------
//#include <atcoder/all>
//using namespace atcoder;
//
//using mint = modint1000000007;
////using mint = modint998244353;
////using mint = modint; // mint::set_mod(m);
//
//istream& operator>>(istream& is, mint& x) { ll x_; is >> x_; x = x_; return is; }
//ostream& operator<<(ostream& os, const mint& x) { os << x.val(); return os; }
//using vm = vector<mint>; using vvm = vector<vm>; using vvvm = vector<vvm>;
////----------------------------------------


#ifndef ATCODER_INTERNAL_CSR_HPP
#define ATCODER_INTERNAL_CSR_HPP 1

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
    namespace internal {

        template <class E> struct csr {
            std::vector<int> start;
            std::vector<E> elist;
            explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
                : start(n + 1), elist(edges.size()) {
                for (auto e : edges) {
                    start[e.first + 1]++;
                }
                for (int i = 1; i <= n; i++) {
                    start[i] += start[i - 1];
                }
                auto counter = start;
                for (auto e : edges) {
                    elist[counter[e.first]++] = e.second;
                }
            }
        };

    }  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_CSR_HPP


#ifndef ATCODER_INTERNAL_QUEUE_HPP
#define ATCODER_INTERNAL_QUEUE_HPP 1

#include <vector>

namespace atcoder {

    namespace internal {

        template <class T> struct simple_queue {
            std::vector<T> payload;
            int pos = 0;
            void reserve(int n) { payload.reserve(n); }
            int size() const { return int(payload.size()) - pos; }
            bool empty() const { return pos == int(payload.size()); }
            void push(const T& t) { payload.push_back(t); }
            T& front() { return payload[pos]; }
            void clear() {
                payload.clear();
                pos = 0;
            }
            void pop() { pos++; }
        };

    }  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_QUEUE_HPP


#ifndef ATCODER_MINCOSTFLOW_HPP
#define ATCODER_MINCOSTFLOW_HPP 1

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

    template <class Cap, class Cost> struct mcf_graph {
    public:
        mcf_graph() {}
        explicit mcf_graph(int n) : _n(n) {}

        int add_edge(int from, int to, Cap cap, Cost cost) {
            assert(0 <= from && from < _n);
            assert(0 <= to && to < _n);
            assert(0 <= cap);
            assert(0 <= cost);
            int m = int(_edges.size());
            _edges.push_back({ from, to, cap, 0, cost });
            return m;
        }

        struct edge {
            int from, to;
            Cap cap, flow;
            Cost cost;
        };

        edge get_edge(int i) {
            int m = int(_edges.size());
            assert(0 <= i && i < m);
            return _edges[i];
        }
        std::vector<edge> edges() { return _edges; }

        std::pair<Cap, Cost> flow(int s, int t) {
            return flow(s, t, std::numeric_limits<Cap>::max());
        }
        std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
            return slope(s, t, flow_limit).back();
        }
        std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
            return slope(s, t, std::numeric_limits<Cap>::max());
        }
        std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
            assert(0 <= s && s < _n);
            assert(0 <= t && t < _n);
            assert(s != t);

            int m = int(_edges.size());
            std::vector<int> edge_idx(m);

            auto g = [&]() {
                std::vector<int> degree(_n), redge_idx(m);
                std::vector<std::pair<int, _edge>> elist;
                elist.reserve(2 * m);
                for (int i = 0; i < m; i++) {
                    auto e = _edges[i];
                    edge_idx[i] = degree[e.from]++;
                    redge_idx[i] = degree[e.to]++;
                    elist.push_back({ e.from, {e.to, -1, e.cap - e.flow, e.cost} });
                    elist.push_back({ e.to, {e.from, -1, e.flow, -e.cost} });
                }
                auto _g = internal::csr<_edge>(_n, elist);
                for (int i = 0; i < m; i++) {
                    auto e = _edges[i];
                    edge_idx[i] += _g.start[e.from];
                    redge_idx[i] += _g.start[e.to];
                    _g.elist[edge_idx[i]].rev = redge_idx[i];
                    _g.elist[redge_idx[i]].rev = edge_idx[i];
                }
                return _g;
            }();

            auto result = slope(g, s, t, flow_limit);

            for (int i = 0; i < m; i++) {
                auto e = g.elist[edge_idx[i]];
                _edges[i].flow = _edges[i].cap - e.cap;
            }

            return result;
        }

    private:
        int _n;
        std::vector<edge> _edges;

        // inside edge
        struct _edge {
            int to, rev;
            Cap cap;
            Cost cost;
        };

        std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
            int s,
            int t,
            Cap flow_limit) {
            // variants (C = maxcost):
            // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
            // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge

            // dual_dist[i] = (dual[i], dist[i])
            std::vector<std::pair<Cost, Cost>> dual_dist(_n);
            std::vector<int> prev_e(_n);
            std::vector<bool> vis(_n);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::vector<int> que_min;
            std::vector<Q> que;
            auto dual_ref = [&]() {
                for (int i = 0; i < _n; i++) {
                    dual_dist[i].second = std::numeric_limits<Cost>::max();
                }
                std::fill(vis.begin(), vis.end(), false);
                que_min.clear();
                que.clear();

                // que[0..heap_r) was heapified
                size_t heap_r = 0;

                dual_dist[s].second = 0;
                que_min.push_back(s);
                while (!que_min.empty() || !que.empty()) {
                    int v;
                    if (!que_min.empty()) {
                        v = que_min.back();
                        que_min.pop_back();
                    }
                    else {
                        while (heap_r < que.size()) {
                            heap_r++;
                            std::push_heap(que.begin(), que.begin() + heap_r);
                        }
                        v = que.front().to;
                        std::pop_heap(que.begin(), que.end());
                        que.pop_back();
                        heap_r--;
                    }
                    if (vis[v]) continue;
                    vis[v] = true;
                    if (v == t) break;
                    // dist[v] = shortest(s, v) + dual[s] - dual[v]
                    // dist[v] >= 0 (all reduced cost are positive)
                    // dist[v] <= (n-1)C
                    Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                    for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                        auto e = g.elist[i];
                        if (!e.cap) continue;
                        // |-dual[e.to] + dual[v]| <= (n-1)C
                        // cost <= C - -(n-1)C + 0 = nC
                        Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                        if (dual_dist[e.to].second - dist_v > cost) {
                            Cost dist_to = dist_v + cost;
                            dual_dist[e.to].second = dist_to;
                            prev_e[e.to] = e.rev;
                            if (dist_to == dist_v) {
                                que_min.push_back(e.to);
                            }
                            else {
                                que.push_back(Q{ dist_to, e.to });
                            }
                        }
                    }
                }
                if (!vis[t]) {
                    return false;
                }

                for (int v = 0; v < _n; v++) {
                    if (!vis[v]) continue;
                    // dual[v] = dual[v] - dist[t] + dist[v]
                    //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) +
                    //         (shortest(s, v) + dual[s] - dual[v]) = - shortest(s,
                    //         t) + dual[t] + shortest(s, v) = shortest(s, v) -
                    //         shortest(s, t) >= 0 - (n-1)C
                    dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
                }
                return true;
            };
            Cap flow = 0;
            Cost cost = 0, prev_cost_per_flow = -1;
            std::vector<std::pair<Cap, Cost>> result = { {Cap(0), Cost(0)} };
            while (flow < flow_limit) {
                if (!dual_ref()) break;
                Cap c = flow_limit - flow;
                for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                    c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
                }
                for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                    auto& e = g.elist[prev_e[v]];
                    e.cap += c;
                    g.elist[e.rev].cap -= c;
                }
                Cost d = -dual_dist[s].first;
                flow += c;
                cost += c * d;
                if (prev_cost_per_flow == d) {
                    result.pop_back();
                }
                result.push_back({ flow, cost });
                prev_cost_per_flow = d;
            }
            return result;
        }
    };

}  // namespace atcoder

#endif  // ATCODER_MINCOSTFLOW_HPP


using namespace atcoder;


void solve() {
	int n;
	cin >> n;

	if (n == 0) exit(0);

	vector<map<int, int>> ids_in(n), ids_out(n); int id = 0;
	vector<tuple<int, int, int, int>> es;

	rep(i, n - 1) {
		int m;
		cin >> m;

		rep(j, m) {
			int x, y, c;
			cin >> x >> y >> c;

			if (!ids_out[i].count(x)) ids_out[i][x] = id++;
			if (!ids_in[i + 1].count(y)) ids_in[i + 1][y] = id++;
			if (!ids_out[i + 1].count(y)) ids_out[i + 1][y] = id++;

			es.emplace_back(ids_out[i][x], ids_in[i + 1][y], INF, c);
			es.emplace_back(ids_in[i + 1][y], ids_out[i + 1][y], 1, 0);
		}
	}
	uniq(es);
	dumpel(ids_in); dumpel(ids_out); dump(es);

	int N = id;
	mcf_graph<int, int> g(N);

	repe(e, es) {
		int s, t, cap, cost;
		tie(s, t, cap, cost) = e;

		g.add_edge(s, t, cap, cost);
	}

	rep(i, n) {
		int bid = -1;
		repe(tmp, ids_out[i]) {
			int t, id;
			tie(t, id) = tmp;

			if (bid != -1) {
				g.add_edge(bid, id, INF, 0);
			}
			bid = id;
		}
	}
//	dump(g);

	int f_lim;
	cin >> f_lim;

	int cap, cost;
	tie(cap, cost) = g.flow(ids_out[0].begin()->second, ids_out[n - 1].rbegin()->second, f_lim);

	cout << cap << " " << cost << endl;
}


int main() {
//	input_from_file("input.txt");
//	output_to_file("output.txt");
	
	while (true) solve();
}
accuracy: 1
time_ms: 30
memory_kb: 4168
final_rank: 1