// code template is in https://github.com/drken1215/algorithm/blob/master/template_minimum.cpp #pragma GCC optimize("Ofast") #pragma GCC optimize("unroll-loops") #include using namespace std; //------------------------------// // Utility //------------------------------// using ll = long long; using i128 = __int128_t; using u128 = __uint128_t; using pint = pair; using pll = pair; using tll = array; using fll = array; using vint = vector; using vll = vector; using dint = deque; using dll = deque; using vvint = vector>; using vvll = vector>; using vpll = vector>; template using min_priority_queue = priority_queue, greater>; template inline bool chmax(S &a, T b) { return (a < b ? a = b, 1 : 0); } template inline bool chmin(S &a, T b) { return (a > b ? a = b, 1 : 0); } template inline auto maxll(S a, T b) { return max(ll(a), ll(b)); } template inline auto minll(S a, T b) { return min(ll(a), ll(b)); } template auto max(const T &a) { return *max_element(a.begin(), a.end()); } template auto min(const T &a) { return *min_element(a.begin(), a.end()); } template auto argmax(const T &a) { return max_element(a.begin(), a.end()) - a.begin(); } template auto argmin(const T &a) { return min_element(a.begin(), a.end()) - a.begin(); } template auto accum(const vector &a) { return accumulate(a.begin(), a.end(), T()); } template auto accum(const deque &a) { return accumulate(a.begin(), a.end(), T()); } #define REP(i, a) for (long long i = 0; i < (long long)(a); i++) #define REP2(i, a, b) for (long long i = a; i < (long long)(b); i++) #define RREP(i, a) for (long long i = (a)-1; i >= (long long)(0); --i) #define RREP2(i, a, b) for (long long i = (b)-1; i >= (long long)(a); --i) #define EB emplace_back #define PF push_front #define PB push_back #define MP make_pair #define FI first #define SE second #define ALL(x) x.begin(), x.end() #define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl // input template istream& operator >> (istream &is, vector &P) { for (int i = 0; i < (int)P.size(); ++i) cin >> P[i]; return is; } template istream& operator >> (istream &is, deque &P) { for (int i = 0; i < (int)P.size(); ++i) cin >> P[i]; return is; } template istream& operator >> (istream &is, vector> &P) { for (int i = 0; i < (int)P.size(); ++i) cin >> P[i]; return is; } // output template ostream& operator << (ostream &s, const pair &P) { return s << '<' << P.first << ", " << P.second << '>'; } template ostream& operator << (ostream &s, const array &P) { return s << '<' << P[0] << "," << P[1] << '>'; } template ostream& operator << (ostream &s, const array &P) { return s << '<' << P[0] << "," << P[1] << "," << P[2] << '>'; } template ostream& operator << (ostream &s, const array &P) { return s << '<' << P[0] << "," << P[1] << "," << P[2] << "," << P[3] << '>'; } template ostream& operator << (ostream &s, const vector &P) { for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; } template ostream& operator << (ostream &s, const deque &P) { for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; } template ostream& operator << (ostream &s, const vector> &P) { for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; } template ostream& operator << (ostream &s, const set &P) { for (auto it : P) { s << "<" << it << "> "; } return s; } template ostream& operator << (ostream &s, const multiset &P) { for (auto it : P) { s << "<" << it << "> "; } return s; } template ostream& operator << (ostream &s, const unordered_set &P) { for (auto it : P) { s << "<" << it << "> "; } return s; } template ostream& operator << (ostream &s, const map &P) { for (auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; } template ostream& operator << (ostream &s, const unordered_map &P) { for (auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; } void yes(bool a) { cout << (a ? "yes" : "no") << endl; } void YES(bool a) { cout << (a ? "YES" : "NO") << endl; } void Yes(bool a) { cout << (a ? "Yes" : "No") << endl; } const vector DX = {1, 0, -1, 0, 1, -1, 1, -1}; const vector DY = {0, 1, 0, -1, 1, -1, -1, 1}; // edge class (for max-flow) template struct FlowEdge { // core members int rev, from, to; FLOW cap, icap, flow; // constructor constexpr FlowEdge() noexcept = default; constexpr FlowEdge(int rev, int from, int to, FLOW cap, FLOW rcap = 0) : rev(rev), from(from), to(to), cap(cap), icap(cap), flow(rcap) { } void reset() { flow -= icap - cap; cap = icap; } // debug friend ostream& operator << (ostream& s, const FlowEdge& e) { return s << e.from << " -> " << e.to << " (" << e.cap << ", " << e.flow << ")"; } }; // graph class (for max-flow) template struct FlowGraph { // core members vector>> list; vector> pos; // pos[i] := {vertex, order of list[vertex]} of i-th edge // constructor FlowGraph(int n = 0) : list(n) { } void init(int n = 0) { list.clear(), list.resize(n); pos.clear(); } void clear() { list.clear(), pos.clear(); } // getter vector> &operator [] (int i) { assert(0 <= i && i < (int)list.size()); return list[i]; } const vector> &operator [] (int i) const { assert(0 <= i && i < (int)list.size()); return list[i]; } size_t size() const noexcept { return list.size(); } FlowEdge &get_rev_edge(const FlowEdge &e) { return list[e.to][e.rev]; } const FlowEdge &get_rev_edge(const FlowEdge &e) const { return list[e.to][e.rev]; } FlowEdge &get_edge(int i) { return list[pos[i].first][pos[i].second]; } const FlowEdge &get_edge(int i) const { return list[pos[i].first][pos[i].second]; } vector> get_edges() const { vector> edges; for (int i = 0; i < (int)pos.size(); ++i) { edges.push_back(get_edge(i)); } return edges; } // change edges void reset() const { for (int i = 0; i < (int)list.size(); ++i) { for (FlowEdge &e : list[i]) e.reset(); } } void change_edge(FlowEdge &e, FLOW new_cap, FLOW new_rcap) { assert(new_cap >= 0 && new_rcap >= 0); FlowEdge &re = get_rev_edge(e); e.cap = new_cap, e.icap = new_cap + new_rcap, e.flow = new_rcap; re.cap = new_rcap, re.icap = new_cap + new_rcap, re.flow = new_cap; } // add_edge void add_edge(int from, int to, FLOW cap, FLOW rcap = 0) { assert(0 <= from && from < (int)list.size() && 0 <= to && to < (int)list.size()); assert(cap >= 0); int from_id = int(list[from].size()), to_id = int(list[to].size()); if (from == to) to_id++; pos.emplace_back(from, from_id); list[from].push_back(FlowEdge(to_id, from, to, cap, rcap)); list[to].push_back(FlowEdge(from_id, to, from, rcap, cap)); } void add_bidirected_edge(int from, int to, FLOW cap) { assert(0 <= from && from < (int)list.size() && 0 <= to && to < (int)list.size()); assert(cap >= 0); add_edge(from, to, cap, cap); } // augment FLOW augment(int s, int t, FLOW up_flow = numeric_limits::max()) { vector seen(size(), false); auto dfs = [&](auto &&dfs, int v, FLOW up_flow) -> FLOW { if (v == t) return up_flow; seen[v] = true; for (int i = 0; i < (int)list[v].size(); i++) { FlowEdge &e = list[v][i], &re = get_rev_edge(e); if (seen[e.to] || e.cap <= 0) continue; FLOW flow = dfs(dfs, e.to, min(up_flow, e.cap)); if (flow > 0) { e.cap -= flow, e.flow += flow; re.cap += flow, re.flow -= flow; return flow; } } return FLOW(0); }; return dfs(dfs, s, up_flow); }; // find reachable nodes from node s (1: s-domain, -1: t-domain, 0: no reach) vector find_cut(int s, int t) const { vector res(size(), 0); auto dfs_s = [&](auto &&dfs_s, int v) -> void { res[v] = 1; for (const auto &e : list[v]) { if (res[e.to] || e.cap <= 0) continue; dfs_s(dfs_s, e.to); } }; auto dfs_t = [&](auto &&dfs_t, int v) -> void { res[v] = -1; for (const auto &e : list[v]) { auto re = get_rev_edge(e); if (res[e.to] || re.cap <= 0) continue; dfs_t(dfs_t, e.to); } }; dfs_s(dfs_s, s), dfs_t(dfs_t, t); return res; } // check if the s-t flow is feasible bool is_feasible(int s, int t) const { vector b(list.size(), FLOW(0)); for (int v = 0; v < (int)list.size(); v++) { for (const auto &e : list[v]) { b[v] += (e.flow - get_rev_edge(e).flow) / 2; } } if (b[s] + b[t] != 0) return false; for (int v = 0; v < (int)list.size(); v++) { if (v != s && v != t && b[v] != FLOW(0)) return false; } return true; } bool is_feasible(int s, int t, FLOW flow) const { vector b(list.size(), FLOW(0)); for (int v = 0; v < (int)list.size(); v++) { for (const auto &e : list[v]) { b[v] += (e.flow - get_rev_edge(e).flow) / 2; } } if (b[s] != flow) return false; if (b[t] != -flow) return false; for (int v = 0; v < (int)list.size(); v++) { if (v != s && v != t && b[v] != FLOW(0)) return false; } return true; } // decompose flow into s-t simple paths and cycles using Path = vector>; pair, vector> decompose(int s, int t) const { struct Arc { int to; FLOW rem; int eidx; }; assert(is_feasible(s, t)); vector> fg(list.size()); for (int v = 0; v < (int)list.size(); v++) { for (int j = 0; j < (int)list[v].size(); j++) { FLOW f = list[v][j].icap - list[v][j].cap; if (f > 0) fg[v].push_back({list[v][j].to, f, j}); } } vector ptr(list.size(), 0), onpath(list.size(), -1); vector> route; vector used; vector paths, cycles; auto next_arc = [&](int v) -> int { while (ptr[v] < (int)fg[v].size() && fg[v][ptr[v]].rem <= 0) ptr[v]++; return (ptr[v] < (int)fg[v].size() ? ptr[v] : -1); }; auto extract = [&](int begin, bool is_cycle) { FLOW mi = numeric_limits::max(); for (int k = begin; k < (int)route.size(); k++) { auto [v, i] = route[k]; mi = min(mi, fg[v][i].rem); } vector> seq; for (int k = begin; k < (int)route.size(); k++) { auto [v, i] = route[k]; fg[v][i].rem -= mi; FlowEdge e = list[v][fg[v][i].eidx]; e.flow = mi; seq.push_back(e); } if (is_cycle) cycles.push_back(std::move(seq)); else paths.push_back(std::move(seq)); }; auto walk = [&](int start, bool stop_at_t) { route.clear(); int v = start; onpath[v] = 0; used.push_back(v); while (true) { int i = next_arc(v), u = fg[v][i].to; route.push_back({v, i}); if (stop_at_t && u == t) { extract(0, false); break; } if (onpath[u] != -1) { extract(onpath[u], true); break; } onpath[u] = (int)route.size(); used.push_back(u); v = u; } for (int w : used) onpath[w] = -1; used.clear(); }; // extract all s-t paths while (next_arc(s) != -1) walk(s, true); // decompose remained circulation into cycles for (int v = 0; v < (int)list.size(); v++) while (next_arc(v) != -1) walk(v, false); return {paths, cycles}; } // debug friend ostream& operator << (ostream& s, const FlowGraph &G) { const auto &edges = G.get_edges(); for (const auto &e : edges) s << e << endl; return s; } }; // Dinic template FLOW Dinic(FlowGraph &G, int s, int t, FLOW limit_flow) { assert(0 <= s && s < (int)G.size() && 0 <= t && t < (int)G.size() && s != t); FLOW current_flow = 0; vector level((int)G.size(), -1), iter((int)G.size(), 0); // Dinic BFS auto bfs = [&]() -> void { level.assign((int)G.size(), -1); level[s] = 0; queue que; que.push(s); while (!que.empty()) { int v = que.front(); que.pop(); for (const FlowEdge &e : G[v]) { if (level[e.to] < 0 && e.cap > 0) { level[e.to] = level[v] + 1; if (e.to == t) return; que.push(e.to); } } } }; // Dinic DFS auto dfs = [&](auto self, int v, FLOW up_flow) { if (v == t) return up_flow; FLOW res_flow = 0; for (int &i = iter[v]; i < (int)G[v].size(); ++i) { FlowEdge &e = G[v][i], &re = G.get_rev_edge(e); if (level[v] >= level[e.to] || e.cap <= 0) continue; FLOW flow = self(self, e.to, min(up_flow - res_flow, e.cap)); if (flow <= 0) continue; res_flow += flow; e.cap -= flow, e.flow += flow; re.cap += flow, re.flow -= flow; if (res_flow == up_flow) break; } return res_flow; }; // flow while (current_flow < limit_flow) { bfs(); if (level[t] < 0) break; iter.assign((int)iter.size(), 0); while (current_flow < limit_flow) { FLOW flow = dfs(dfs, s, limit_flow - current_flow); if (flow <= 0) break; current_flow += flow; } } return current_flow; }; template FLOW Dinic(FlowGraph &G, int s, int t) { return Dinic(G, s, t, numeric_limits::max()); } //------------------------------// // Solver //------------------------------// int main() { cin.tie(nullptr); ios_base::sync_with_stdio(false); ll N, M, D, INF = 1LL<<40; cin >> N >> M >> D; vll U(M), V(M), P(M), Q(M), W(M); REP(i, M) cin >> U[i] >> V[i] >> P[i] >> Q[i] >> W[i], U[i]--, V[i]--, P[i] -= D; vll ts{-INF, INF}; REP(i, M) ts.EB(P[i]), ts.EB(Q[i]); sort(ALL(ts)); ts.erase(unique(ALL(ts)), ts.end()); ll T = ts.size(); FlowGraph G(N * T); ll s = 0, t = N * T - 1; REP(i, N) REP(j, T-1) { ll v = i * T + j; ll v2 = i * T + j+1; G.add_edge(v, v2, INF); // ll j2 = lower_bound(ALL(ts), ts[j] + D) - ts.begin(); // if (j2 == j) j2++; // if (j2 < T) { // ll v2 = i * T + j2; // G.add_edge(v, v2, INF); // } //cout << j << " -> " << j2 << endl; } // REP(i, N) REP(j, T) REP2(k, j+1, T) { // ll v = i * T + j; // if (ts[k] - ts[j] >= D) { // ll v2 = i * T + k; // G.add_edge(v, v2, INF); // } // } REP(i, M) { ll p = lower_bound(ALL(ts), P[i]) - ts.begin(); ll q = lower_bound(ALL(ts), Q[i]) - ts.begin(); ll u = U[i] * T + p, v = V[i] * T + q; G.add_edge(u, v, W[i]); } //COUT(G); COUT(s); COUT(t); auto res = Dinic(G, s, t); //COUT(G); COUT(D); COUT(ts); cout << res << endl; }