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

// ===================================================================
// This file contains a collection of Dinic (Maximum Flow) algorithms.
// Each function/class is ready to be used as a "black box".
//
// Read the comments above each one to understand:
//   - What it solves
//   - What input it expects
//   - What it returns
//   - Time complexity
//   - Important constraints / assumptions
//   - Explanation of common terms (Jargon) to make it easy for beginners.
// ===================================================================

// ===================================================================
// JARGON / TERMINOLOGY EXPLANATION (Read this first!)
// ===================================================================
// 1. Flow Network: A directed graph where each edge has a "capacity"
//    (maximum amount it can carry). We send "flow" (like water or data)
//    from a "Source" (starting point) to a "Sink" (ending point).
//
// 2. Source (s): The node where the flow originates.
// 3. Sink (t): The node where the flow ends.
// 4. Capacity (cap): The maximum amount of flow that can pass through an edge.
// 5. Reverse Edge (Residual Edge): An artificial edge added by the algorithm
//    that allows it to "undo" or reroute flow if a better path is found later.
// 6. Residual Graph: The original graph plus all the reverse edges.
// 7. Level Graph (BFS Layers): A simplified graph where we only keep edges
//    that go from a node in the current BFS layer to the next layer.
//    This helps the algorithm find the shortest augmenting paths.
// 8. Blocking Flow: Sending as much flow as possible through the current
//    Level Graph before rebuilding it.
// 9. Current Arc Optimization: A trick that remembers which edges have
//    already been completely used (saturated) in the current blocking flow,
//    so we don't waste time checking them again.
// 10. Min Cut: The minimum total capacity of edges we need to remove to
//     completely disconnect the Source from the Sink.
// 11. Saturated Edge: An edge where the remaining capacity (cap) is zero.
// ===================================================================

// ===================================================================
// MAIN DINIC CLASS (The core engine)
// ===================================================================
// What it solves:
//   Computes the Maximum Flow in a directed graph.
//
// Input:
//   - Number of nodes (n) in the graph (nodes are 0-indexed).
//
// How to use:
//   1. Create an object: Dinic dinic(number_of_nodes);
//   2. Add edges using addEdge(u, v, capacity).
//   3. Call maxFlow(source, sink) to get the maximum flow value.
//
// Time Complexity:
//   O(E * V^2) in the worst case for general graphs.
//   In practice, it is very fast, especially on sparse graphs.
//   For Bipartite Matching, it runs in O(E * sqrt(V)).
//   NOTE: The "current arc optimization" speeds it up significantly.
//
// Notes:
//   - All capacities and flows are stored as 'long long' to prevent overflow.
//   - If you have an undirected edge, you should call addUndirectedEdge
//     (provided in the Tricks section), or add two directed edges.
//   - The graph is 0-indexed. If your nodes are 1-indexed, subtract 1 from
//     every node index.
// ===================================================================

struct Dinic {
    struct Edge {
        int to;          // The node this edge goes to
        int rev;         // Index of the reverse edge in the adjacency list of 'to'
        long long cap;   // Remaining capacity of this edge
    };

    int n;                      // Number of nodes
    vector<vector<Edge>> adj;   // Adjacency list
    vector<int> level;          // BFS level of each node
    vector<int> it;             // Pointer for current arc optimization

    // Constructor: initializes the graph with 'n' nodes.
    Dinic(int n) : n(n), adj(n), level(n), it(n) {}

    // -----------------------------------------------------------------
    // addEdge
    // -----------------------------------------------------------------
    // What it does:
    //   Adds a directed edge from node 'u' to node 'v' with a maximum
    //   capacity 'cap'. Flow can only travel from 'u' to 'v' in the
    //   original network.
    //
    // Input:
    //   u   : source node of the edge
    //   v   : destination node of the edge
    //   cap : maximum capacity of this edge (must be >= 0)
    //
    // Output:
    //   None (modifies the graph internally).
    //
    // Time complexity:
    //   O(1)
    //
    // Notes:
    //   - This automatically adds a reverse edge (with 0 capacity) for
    //     the algorithm to work. You should NOT manually modify or call
    //     this reverse edge directly.
    //   - If cap == 0, the edge is useless but can be safely added.
    // -----------------------------------------------------------------
    void addEdge(int u, int v, long long cap) {
        Edge a{ v, (int)adj[v].size(), cap };
        Edge b{ u, (int)adj[u].size(), 0 };
        adj[u].push_back(a);
        adj[v].push_back(b);
    }

    // -----------------------------------------------------------------
    // bfs (Breadth First Search) - Internal helper
    // -----------------------------------------------------------------
    // What it does:
    //   Constructs the "Level Graph" by calculating the shortest distance
    //   (in terms of number of edges) from the source to every other node
    //   using only edges that still have remaining capacity (> 0).
    //
    // Input:
    //   s : the source node
    //   t : the sink node (not strictly needed for BFS, but we stop early if we reach it)
    //
    // Output:
    //   Returns 'true' if the sink 't' is reachable from 's', 'false' otherwise.
    //
    // Time complexity:
    //   O(V + E)
    //
    // Notes:
    //   You don't need to call this manually; it is called inside maxFlow().
    // -----------------------------------------------------------------
    bool bfs(int s, int t) {
        fill(level.begin(), level.end(), -1);
        queue<int> q;
        level[s] = 0;
        q.push(s);

        while (!q.empty()) {
            int u = q.front();
            q.pop();

            for (const Edge& e : adj[u]) {
                if (e.cap > 0 && level[e.to] == -1) {
                    level[e.to] = level[u] + 1;
                    if (e.to == t) {
                        // We don't return immediately here to allow full leveling,
                        // but returning early is also safe. We keep standard BFS.
                    }
                    q.push(e.to);
                }
            }
        }
        return level[t] != -1;
    }

    // -----------------------------------------------------------------
    // dfs (Depth First Search) - Internal helper
    // -----------------------------------------------------------------
    // What it does:
    //   Sends as much flow as possible from node 'u' to the sink 't'
    //   using the current Level Graph. It only uses edges that go from
    //   the current BFS layer to the next BFS layer (level[v] == level[u] + 1).
    //
    // Input:
    //   u : current node
    //   t : sink node
    //   f : maximum amount of flow we are allowed to push through this path
    //
    // Output:
    //   Returns the amount of flow actually pushed to the sink.
    //
    // Time complexity:
    //   O(E) per DFS call, but with current arc optimization, total is O(E * V).
    //
    // Notes:
    //   - This is called repeatedly by maxFlow().
    //   - "Current Arc" optimization is implemented using the 'it' pointer.
    //     It remembers which edges are already saturated so we skip them.
    // -----------------------------------------------------------------
    long long dfs(int u, int t, long long f) {
        if (u == t) return f;

        for (int &i = it[u]; i < (int)adj[u].size(); i++) {
            Edge &e = adj[u][i];

            if (e.cap > 0 && level[e.to] == level[u] + 1) {
                long long pushed = dfs(e.to, t, min(f, e.cap));
                if (pushed > 0) {
                    e.cap -= pushed;
                    adj[e.to][e.rev].cap += pushed;
                    return pushed;
                }
            }
        }
        return 0;
    }

    // -----------------------------------------------------------------
    // maxFlow
    // -----------------------------------------------------------------
    // What it solves:
    //   Computes the maximum amount of flow that can be sent from the
    //   'source' node to the 'sink' node in the network.
    //
    // Input:
    //   s : the source node index
    //   t : the sink node index
    //
    // Output:
    //   Returns the total maximum flow value (long long).
    //
    // Time complexity:
    //   O(E * V^2) in the worst case.
    //
    // Constraints:
    //   - Source and sink must be different nodes (s != t).
    //   - All capacities must be non-negative.
    //
    // Notes:
    //   - After this function finishes, the graph will contain the residual
    //     graph. The remaining capacities in the 'adj' list represent
    //     the residual capacities.
    // -----------------------------------------------------------------
    long long maxFlow(int s, int t) {
        long long flow = 0;
        const long long INF = 4e18; // A very large number

        while (bfs(s, t)) {
            fill(it.begin(), it.end(), 0);
            while (true) {
                long long pushed = dfs(s, t, INF);
                if (pushed == 0) break;
                flow += pushed;
            }
        }
        return flow;
    }

    // -----------------------------------------------------------------
    // getReachableNodes (For Min Cut)
    // -----------------------------------------------------------------
    // What it solves:
    //   After computing the maximum flow, this function finds all nodes
    //   that are still reachable from the 'source' in the residual graph.
    //   The set of these nodes defines the "Source Side" of the minimum cut.
    //
    // Input:
    //   s : the source node index
    //
    // Output:
    //   Returns a vector of booleans (size = n) where 'true' means the node
    //   is reachable from the source in the residual graph.
    //
    // Time complexity:
    //   O(V + E)
    //
    // How to find the Min Cut edges:
    //   Iterate over all original edges (u->v). If reachable[u] is true
    //   and reachable[v] is false, then this edge is part of the minimum cut.
    //
    // Constraints:
    //   - Must be called AFTER running maxFlow(s, t).
    // -----------------------------------------------------------------
    vector<bool> getReachableNodes(int s) {
        vector<bool> reachable(n, false);
        queue<int> q;
        q.push(s);
        reachable[s] = true;

        while (!q.empty()) {
            int u = q.front();
            q.pop();

            for (const Edge& e : adj[u]) {
                if (e.cap > 0 && !reachable[e.to]) {
                    reachable[e.to] = true;
                    q.push(e.to);
                }
            }
        }
        return reachable;
    }
};

// ===================================================================
// 1) BIPARTITE MATCHING (Using Max Flow)
// ===================================================================
// What it solves:
//   Given two disjoint sets of nodes (Left and Right), and allowed
//   connections between them, find the maximum number of pairs
//   (one from Left, one from Right) such that each node is used at most once.
//   Example: Assigning workers to jobs.
//
// Input:
//   - n: number of nodes on the Left side (indexed 0..n-1)
//   - m: number of nodes on the Right side (indexed 0..m-1)
//   - edges: a vector of pairs (u, v) meaning Left-node 'u' can connect to Right-node 'v'.
//
// Output:
//   - Returns the maximum number of matches (pairs).
//   - If you need the actual matching pairs, you would need to trace the flow
//     (this function only returns the count).
//
// Time complexity:
//   O(E * sqrt(V)) because Dinic on bipartite graphs is very fast.
//   Where V = n + m + 2 (plus source and sink), E = number of edges.
//
// Constraints:
//   - n, m >= 0.
//   - Node indices must be in the valid ranges.
//
// Notes:
//   - This function builds the flow network internally.
//   - The source is connected to all Left nodes (capacity 1).
//   - Left nodes connect to Right nodes (capacity 1).
//   - Right nodes connect to the sink (capacity 1).
// -----------------------------------------------------------------
int maxBipartiteMatching(int n, int m, const vector<pair<int, int>>& edges) {
    int totalNodes = n + m + 2;
    int source = n + m;
    int sink = source + 1;

    Dinic dinic(totalNodes);

    // Connect source to left nodes
    for (int u = 0; u < n; u++) {
        dinic.addEdge(source, u, 1);
    }

    // Connect left to right
    for (auto [u, v] : edges) {
        // Right nodes are offset by n to avoid index collision with left nodes
        dinic.addEdge(u, n + v, 1);
    }

    // Connect right nodes to sink
    for (int v = 0; v < m; v++) {
        dinic.addEdge(n + v, sink, 1);
    }

    long long flow = dinic.maxFlow(source, sink);
    return (int)flow;
}

// ===================================================================
// 2) MINIMUM PATH COVER IN A DAG (Using Max Flow)
// ===================================================================
// What it solves:
//   Given a Directed Acyclic Graph (DAG), find the minimum number of
//   vertex-disjoint paths needed to cover all vertices.
//   Each vertex belongs to exactly one path.
//
// Input:
//   - n: number of vertices (0-indexed).
//   - edges: a vector of pairs (u, v) representing a directed edge u -> v.
//
// Output:
//   - Returns the minimum number of paths required to cover all nodes.
//
// Time complexity:
//   O(E * sqrt(V)) using Dinic.
//
// Constraint:
//   - The graph must be a DAG (no cycles). If there are cycles, the
//     mathematical reduction doesn't hold.
//   - Paths are vertex-disjoint (no vertex appears in more than one path).
//
// Idea (explained simply):
//   The minimum path cover equals (Total Vertices) - (Maximum Bipartite Matching).
//   We create a bipartite graph where the left side contains all original nodes,
//   and the right side contains copies of all original nodes.
//   For every edge u -> v in the DAG, we add an edge from Left(u) to Right(v).
//   Running max matching gives the maximum number of edges we can "chain"
//   together, which reduces the number of paths.
// -----------------------------------------------------------------
int minPathCoverDAG(int n, const vector<pair<int, int>>& edges) {
    // Build the bipartite graph (Left: 0..n-1, Right: 0..n-1)
    // We reuse the bipartite matching function.
    vector<pair<int, int>> bipEdges;
    for (auto [u, v] : edges) {
        bipEdges.push_back({u, v}); // Left side u, Right side v (both use same indices)
    }

    int maxMatch = maxBipartiteMatching(n, n, bipEdges);
    return n - maxMatch;
}

// ===================================================================
// TRICKS & ADVANCED PATTERNS (ECPC/ACPC Favorites)
// ===================================================================

// 3.1) SUPER SOURCE AND SUPER SINK
// -----------------------------------------------------------------
// What it solves:
//   When you have multiple sources or multiple sinks, you can connect
//   all sources to a single "Super Source" (with INFINITE capacity), and
//   connect all sinks to a single "Super Sink" (with INFINITE capacity).
//
// How to use (Example):
//   int N = ...; int S = N; int T = N+1; // create two new nodes
//   Dinic dinic(N+2);
//   for source in list_of_sources: dinic.addEdge(S, source, INF);
//   for sink in list_of_sinks: dinic.addEdge(sink, T, INF);
//   // Add your normal edges here...
//   long long ans = dinic.maxFlow(S, T);
//
// Input:
//   - You don't call a function for this; it's a pattern.
//   - Just add edges from SuperSource to all sources, and all sinks to SuperSink.
//
// Output:
//   - The result of maxFlow(SuperSource, SuperSink) is the answer.
//
// Notes:
//   - Use INF = 4e18 (or a very large number bigger than any possible flow).
// -----------------------------------------------------------------

// 3.2) NODE SPLITTING (Vertex Capacities)
// -----------------------------------------------------------------
// What it solves:
//   By default, only edges have capacities. If you need to limit the
//   amount of flow that passes THROUGH a specific node, you must split it.
//
// How to use (Example):
//   For a node 'v' with capacity 'cap', split it into two nodes:
//   v_in = v * 2, and v_out = v * 2 + 1 (or any other indexing scheme).
//   Add an edge: addEdge(v_in, v_out, cap).
//   For any incoming edge (u -> v), add u_out -> v_in.
//   For any outgoing edge (v -> w), add v_out -> w_in.
//
// Input:
//   - You don't call a function for this; it's a pattern.
//
// Notes:
//   - Make sure to allocate enough nodes (2 * number_of_original_nodes).
// -----------------------------------------------------------------

// 3.3) MAXIMUM WEIGHT CLOSURE (Min Cut Application)
// -----------------------------------------------------------------
// What it solves:
//   You have a set of projects (nodes). Each project gives a certain profit
//   (can be positive or negative). There are dependencies: to take project A,
//   you must take project B. Find the maximum total profit you can achieve.
//
// Idea (simplified):
//   - Positive profit projects are connected from Source with capacity = profit.
//   - Negative profit projects are connected to Sink with capacity = -profit.
//   - Dependencies (A depends on B) are added as edges (A -> B) with INF capacity.
//   - Answer = (Sum of all positive profits) - maxFlow(Source, Sink).
//
// Input:
//   - n: number of projects.
//   - profits: vector of long long (size n), where profits[i] is the profit (can be negative).
//   - deps: vector of pairs (a, b) meaning "if you take 'a', you must take 'b'".
//
// Output:
//   - Returns the maximum achievable total profit.
//
// Time complexity:
//   O(maxFlow) on a graph with n+2 nodes.
//
// Notes:
//   - This is a classic problem in competitive programming.
//   - If you don't understand the math, just follow the pattern.
// -----------------------------------------------------------------
long long maxWeightClosure(int n, const vector<long long>& profits, const vector<pair<int, int>>& deps) {
    int S = n;
    int T = n + 1;
    Dinic dinic(n + 2);

    long long totalPositive = 0;
    const long long INF = 4e18;

    for (int i = 0; i < n; i++) {
        if (profits[i] > 0) {
            dinic.addEdge(S, i, profits[i]);
            totalPositive += profits[i];
        } else if (profits[i] < 0) {
            dinic.addEdge(i, T, -profits[i]); // capacity is positive
        }
    }

    for (auto [a, b] : deps) {
        // If we take 'a', we must take 'b'.
        // Edge a -> b with INF capacity means cutting this edge is too expensive,
        // so the min cut won't separate a (source side) from b (sink side)
        // unless b is cut off from the source.
        dinic.addEdge(a, b, INF);
    }

    long long minCut = dinic.maxFlow(S, T);
    return totalPositive - minCut;
}

// 3.4) FLOW WITH LOWER BOUNDS (Feasible Flow / Circulation)
// -----------------------------------------------------------------
// What it solves:
//   Sometimes, edges don't just have a maximum capacity, but also a
//   MINIMUM required flow (lower bound). We need to check if it's
//   possible to send flow satisfying all lower and upper bounds.
//   This function can handle both pure circulation (no source/sink)
//   and standard s‑t flow with lower bounds.
//
// Input:
//   - n: number of nodes.
//   - edges: a vector of LowerBoundEdge (u, v, lower, upper).
//            Means: edge from u to v must carry at least 'lower' and at most 'upper' flow.
//   - s: (optional) source node for s‑t flow. Use -1 for circulation (default).
//   - t: (optional) sink node for s‑t flow. Use -1 for circulation (default).
//
// Output:
//   - Returns 'true' if a feasible flow exists, 'false' otherwise.
//   - If 'true', the residual graph will contain the solution (flow values
//     can be recovered if needed).
//
// Time complexity:
//   O(maxFlow) on a graph with n+2 nodes and E edges.
//
// How it works (simplified):
//   1. Create a new graph with a Super Source (SS) and Super Sink (TT).
//   2. For each edge (u->v) with [L, U]:
//        - Add edge (u -> v) with capacity (U - L). (The adjustable part).
//        - Store the demand: demand[u] -= L; demand[v] += L.
//   3. After processing all edges, for each node i:
//        - If demand[i] > 0: add edge (SS -> i) with capacity demand[i].
//        - If demand[i] < 0: add edge (i -> TT) with capacity -demand[i].
//   4. If s and t are given (not -1), add an edge (t -> s) with INF capacity
//      to convert the problem into a circulation.
//   5. Run maxFlow(SS, TT). If the flow equals the sum of positive demands,
//      then a feasible solution exists.
//
// Constraints:
//   - 0 <= lower <= upper.
//   - Nodes are 0-indexed.
//   - If s and t are provided, they must be valid nodes and different.
//
// Notes:
//   - This function does NOT return the actual flow values on edges,
//     but the residual graph can be used to reconstruct them.
//   - For pure circulation, call with s = -1, t = -1 (or omit the parameters).
// -----------------------------------------------------------------
struct LowerBoundEdge {
    int u, v;
    long long lower, upper;
};

bool feasibleFlowWithLowerBounds(int n, const vector<LowerBoundEdge>& edges, int s = -1, int t = -1) {
    int SS = n;
    int TT = n + 1;
    Dinic dinic(n + 2);

    vector<long long> demand(n, 0);
    const long long INF = 4e18;

    for (const auto& e : edges) {
        demand[e.u] -= e.lower;
        demand[e.v] += e.lower;
        dinic.addEdge(e.u, e.v, e.upper - e.lower);
    }

    long long totalPositiveDemand = 0;
    for (int i = 0; i < n; i++) {
        if (demand[i] > 0) {
            dinic.addEdge(SS, i, demand[i]);
            totalPositiveDemand += demand[i];
        } else if (demand[i] < 0) {
            dinic.addEdge(i, TT, -demand[i]);
        }
    }

    // If we have a specific source and sink, add an infinite edge from sink to source
    // to make it a circulation problem.
    if (s != -1 && t != -1) {
        dinic.addEdge(t, s, INF);
    }

    long long maxflow = dinic.maxFlow(SS, TT);
    return maxflow == totalPositiveDemand;
}

// ===================================================================
// 4) MISCELLANEOUS UTILITY WRAPPERS
// ===================================================================

// 4.1) addUndirectedEdge
// -----------------------------------------------------------------
// What it solves:
//   Adds an undirected edge between 'u' and 'v' with capacity 'cap'.
//   This means flow can go from u to v up to cap, and from v to u up to cap,
//   but the net flow (u->v minus v->u) cannot exceed cap in magnitude.
//   (i.e., the total flow crossing the edge in either direction is bounded by cap).
//
// Input:
//   u, v : the two nodes
//   cap  : the maximum net capacity in either direction.
//
// How to use:
//   dinic.addUndirectedEdge(u, v, cap);
//
// Notes:
//   Internally, it adds two directed edges each with capacity 'cap'.
//   This correctly models an undirected edge because positive flow in one
//   direction cancels negative flow in the other direction in the residual graph.
//   If you need independent capacities (u->v with cap1, v->u with cap2),
//   just call addEdge(u, v, cap1) and addEdge(v, u, cap2) separately.
// -----------------------------------------------------------------
void addUndirectedEdge(Dinic& dinic, int u, int v, long long cap) {
    dinic.addEdge(u, v, cap);
    dinic.addEdge(v, u, cap);
}

// 4.2) getMinCutEdges (Helper)
// -----------------------------------------------------------------
// What it solves:
//   Given a Dinic graph after running maxFlow, and the reachable nodes,
//   it returns a vector of the original edges that form the minimum cut.
//
// Input:
//   - dinic: the Dinic object (must have run maxFlow() already).
//   - reachable: the boolean vector from dinic.getReachableNodes(source).
//   - originalEdges: a vector of the original directed edges that were added.
//     (You need to store them when you call addEdge if you want to extract them).
//
// Output:
//   - vector of pairs (u, v) representing the edges in the min cut.
//
// Note:
//   Since we don't store the original edges in the class by default,
//   this is just a demonstration pattern.
// -----------------------------------------------------------------
// vector<pair<int, int>> getMinCutEdges(Dinic& dinic, vector<bool>& reachable) {
//     vector<pair<int, int>> cutEdges;
//     for (int u = 0; u < dinic.n; u++) {
//         if (!reachable[u]) continue;
//         for (const auto& e : dinic.adj[u]) {
//             // We need to know if 'e' is a forward edge.
//             // Since we don't track that, it's easier to store the original edges list.
//             // Just iterate over your stored original edges and check
//             // reachable[edge.u] && !reachable[edge.v].
//         }
//     }
//     return cutEdges;
// }

// ===================================================================
// main() with example usage (Black-box testing)
// ===================================================================

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // Example 1: Simple Max Flow
    // Nodes: 0, 1, 2, 3. Source = 0, Sink = 3.
    // Edges: 0->1 (10), 0->2 (10), 1->3 (10), 2->3 (10), 1->2 (5).
    cout << "=== Example 1: Simple Max Flow ===\n";
    {
        Dinic dinic(4);
        dinic.addEdge(0, 1, 10);
        dinic.addEdge(0, 2, 10);
        dinic.addEdge(1, 3, 10);
        dinic.addEdge(2, 3, 10);
        dinic.addEdge(1, 2, 5);

        long long flow = dinic.maxFlow(0, 3);
        cout << "Max Flow: " << flow << "\n"; // Expected: 20 (Path 0-1-3 and 0-2-3)
    }

    // Example 2: Bipartite Matching
    // Left: 0, 1. Right: 0, 1.
    // Edges: (0,0), (0,1), (1,0).
    cout << "\n=== Example 2: Bipartite Matching ===\n";
    {
        vector<pair<int, int>> edges = {{0, 0}, {0, 1}, {1, 0}};
        int matches = maxBipartiteMatching(2, 2, edges);
        cout << "Max Matches: " << matches << "\n"; // Expected: 2
    }

    // Example 3: Max Weight Closure
    // Projects: A(profit 10), B(profit -5), C(profit 6).
    // Dependencies: A depends on B, C depends on B.
    cout << "\n=== Example 3: Max Weight Closure ===\n";
    {
        vector<long long> profits = {10, -5, 6};
        vector<pair<int, int>> deps = {{0, 1}, {2, 1}}; // 0->1, 2->1
        long long maxProfit = maxWeightClosure(3, profits, deps);
        cout << "Max Profit: " << maxProfit << "\n"; // Expected: 11 (Take A, B, C. Sum=11)
    }

    // Example 4: Feasible Flow with Lower Bounds (Circulation)
    cout << "\n=== Example 4: Feasible Flow with Lower Bounds (Circulation) ===\n";
    {
        // Nodes: 0, 1, 2.
        // Edge 0->1: lower=5, upper=10
        // Edge 1->2: lower=5, upper=10
        // Edge 2->0: lower=5, upper=10 (to make a circulation)
        vector<LowerBoundEdge> edges = {
            {0, 1, 5, 10},
            {1, 2, 5, 10},
            {2, 0, 5, 10}
        };
        bool feasible = feasibleFlowWithLowerBounds(3, edges); // circulation
        cout << "Feasible: " << (feasible ? "Yes" : "No") << "\n"; // Expected: Yes
    }

    // Example 5: Feasible Flow with Lower Bounds (s-t flow)
    cout << "\n=== Example 5: Feasible Flow with Lower Bounds (s-t) ===\n";
    {
        // Nodes: 0,1,2. Source=0, Sink=2.
        // Edge 0->1: lower=2, upper=5
        // Edge 1->2: lower=3, upper=6
        // Also need an edge 2->0 with lower=0, upper=INF to make circulation? Actually for s-t,
        // we add the infinite edge automatically when we pass s and t.
        vector<LowerBoundEdge> edges = {
            {0, 1, 2, 5},
            {1, 2, 3, 6}
        };
        bool feasible = feasibleFlowWithLowerBounds(3, edges, 0, 2);
        cout << "Feasible: " << (feasible ? "Yes" : "No") << "\n"; // Expected: Yes (can send 3-5 flow)
    }

    return 0;
}