#line 1 "template/template.hpp" #include #if __has_include() #include #endif using namespace std; using int64 = long long; const int64 infll = (1LL << 62) - 1; const int inf = (1 << 30) - 1; struct IoSetup { IoSetup() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(10); cerr << fixed << setprecision(10); } } iosetup; template ostream& operator<<(ostream& os, const pair& p) { os << p.first << " " << p.second; return os; } template istream& operator>>(istream& is, pair& p) { is >> p.first >> p.second; return is; } template ostream& operator<<(ostream& os, const vector& v) { for (size_t i = 0; i < v.size(); i++) { os << v[i] << (i + 1 != v.size() ? " " : ""); } return os; } template istream& operator>>(istream& is, vector& v) { for (T& in : v) is >> in; return is; } template bool chmax(T1& a, T2 b) { return a < b && (a = b, true); } template bool chmin(T1& a, T2 b) { return a > b && (a = b, true); } template vector make_v(size_t a) { return vector(a); } template auto make_v(size_t a, Ts... ts) { return vector(ts...))>(a, make_v(ts...)); } template enable_if_t == 0> fill_v(T& t, const V& v) { t = v; } template enable_if_t != 0> fill_v(T& t, const V& v) { for (auto& e : t) fill_v(e, v); } template struct FixPoint : F { explicit FixPoint(F&& f) : F(std::forward(f)) {} template decltype(auto) operator()(Args&&... args) const { return F::operator()(*this, std::forward(args)...); } }; template decltype(auto) MFP(F&& f) { return FixPoint{std::forward(f)}; } #include #include #include #include #include #include class Stack { private: const int N, H; std::vector node; public: Stack(int N, int H) : N(N), H(H), node(N + H) { clear(); } bool empty(int h) const { return node[N + h] == N + h; } int top(int h) const { return node[N + h]; } void pop(int h) { node[N + h] = node[node[N + h]]; } void push(int h, int u) { node[u] = node[N + h]; node[N + h] = u; } void clear() { std::iota(node.begin() + N, node.end(), N); } }; class List { public: struct Node { int prev, next; }; const int N, H; std::vector dat; List(int N, int H) : N(N), H(H), dat(N + H) { clear(); } bool empty(int h) const { return dat[N + h].next == N + h; } bool more_one(int h) const { return dat[N + h].prev != dat[N + h].next; } void insert(int h, int u) { int s = N + h; dat[u].prev = dat[s].prev; dat[u].next = s; dat[dat[s].prev].next = u; dat[s].prev = u; } void erase(int u) { dat[dat[u].prev].next = dat[u].next; dat[dat[u].next].prev = dat[u].prev; } void clear() { for (int i = N; i < N + H; ++i) { dat[i].prev = dat[i].next = i; } } }; template struct PushRelabel { static_assert(std::is_signed_v); struct Edge { int to; int next; Cap cap; bool isrev; int idx; }; int V; int height = -1; int relabels = 0; std::vector ex; std::vector potential; std::vector cur_edge; std::vector head; List all_ver; Stack act_ver; std::vector edges; explicit PushRelabel(int V) : V(V), ex(V, Cap(0)), potential(V, 0), cur_edge(V, -1), head(V, -1), all_ver(V, V), act_ver(V, V) {} void reserve_edges(int M) { edges.reserve(2 * M); } void add_edge(int from, int to, Cap cap, int idx = -1) { assert(0 <= from and from < V); assert(0 <= to and to < V); assert(cap >= 0); if (from == to) return; edges.push_back({to, head[from], cap, false, idx}); head[from] = (int)edges.size() - 1; edges.push_back({from, head[to], Cap(0), true, idx}); head[to] = (int)edges.size() - 1; } int calc_active(int t) { height = -1; for (int i = 0; i < V; ++i) { if (potential[i] < V) { cur_edge[i] = head[i]; height = std::max(height, potential[i]); all_ver.insert(potential[i], i); if (ex[i] > 0 and i != t) { act_ver.push(potential[i], i); } } else { potential[i] = V + 1; } } return height; } void bfs(int t) { for (int i = 0; i < V; ++i) { potential[i] = std::max(potential[i], V); } potential[t] = 0; std::queue que; que.push(t); while (!que.empty()) { int v = que.front(); que.pop(); for (int id = head[v]; id != -1; id = edges[id].next) { const Edge& e = edges[id]; // e.to -> v に残余容量があるか if (potential[e.to] == V && edges[id ^ 1].cap > 0) { potential[e.to] = potential[v] + 1; que.push(e.to); } } } } int init(int s, int t) { potential[s] = V + 1; bfs(t); for (int id = head[s]; id != -1; id = edges[id].next) { Edge& e = edges[id]; if (potential[e.to] < V) { Cap f = e.cap; edges[id ^ 1].cap += f; ex[s] -= f; ex[e.to] += f; } e.cap = 0; } return calc_active(t); } bool push(int u, int t, int id) { Edge& e = edges[id]; Cap f = std::min(e.cap, ex[u]); if (f <= 0) return ex[u] == 0; int v = e.to; e.cap -= f; edges[id ^ 1].cap += f; ex[u] -= f; ex[v] += f; if (ex[v] == f && v != t) { act_ver.push(potential[v], v); } return ex[u] == 0; } int discharge(int u, int t) { for (int& id = cur_edge[u]; id != -1; id = edges[id].next) { Edge& e = edges[id]; if (e.cap > 0 && potential[u] == potential[e.to] + 1) { if (push(u, t, id)) { return potential[u]; } } } return relabel(u); } int global_relabel(int t) { bfs(t); all_ver.clear(); act_ver.clear(); return calc_active(t); } void gap_relabel(int u) { for (int h = potential[u]; h <= height; ++h) { int sentinel = V + h; for (int v = all_ver.dat[sentinel].next; v < V;) { int nxt = all_ver.dat[v].next; potential[v] = V + 1; v = nxt; } all_ver.dat[sentinel].prev = sentinel; all_ver.dat[sentinel].next = sentinel; } } int relabel(int u) { ++relabels; int prv = potential[u]; int nxt_h = V; int best_edge = -1; for (int id = head[u]; id != -1; id = edges[id].next) { const Edge& e = edges[id]; if (e.cap > 0 && nxt_h > potential[e.to] + 1) { nxt_h = potential[e.to] + 1; best_edge = id; } } cur_edge[u] = best_edge; if (all_ver.more_one(prv)) { all_ver.erase(u); if (nxt_h == V) { potential[u] = V + 1; return prv; } potential[u] = nxt_h; act_ver.push(nxt_h, u); all_ver.insert(nxt_h, u); height = std::max(height, nxt_h); } else { gap_relabel(u); height = prv - 1; return height; } return nxt_h; } Cap max_flow(int s, int t) { assert(s != t); int level = init(s, t); while (level >= 0) { if (act_ver.empty(level)) { --level; continue; } int u = act_ver.top(level); act_ver.pop(level); level = discharge(u, t); if (relabels * 2 >= V) { level = global_relabel(t); relabels = 0; } } return ex[t]; } }; int main() { int N, M, S, T; cin >> N >> M >> S >> T; PushRelabel< int64 > g(N); for (int i = 0; i < M; i++) { int a, b, c; cin >> a >> b >> c; --a, --b; g.add_edge(a, b, c); } cout << g.max_flow(S - 1, T - 1) << endl; }