diff --git a/AtCoderLibrary/MaxFlow.cs b/AtCoderLibrary/MaxFlow.cs
new file mode 100644
index 00000000..eb4d87f6
--- /dev/null
+++ b/AtCoderLibrary/MaxFlow.cs
@@ -0,0 +1,379 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace AtCoder
+{
+ ///
+ /// 最大フロー問題 を解くライブラリ(int版)です。
+ ///
+ public class MFGraphInt : MFGraph { public MFGraphInt(int n) : base(n) { } }
+
+ ///
+ /// 最大フロー問題 を解くライブラリ(long版)です。
+ ///
+ public class MFGraphLong : MFGraph { public MFGraphLong(int n) : base(n) { } }
+
+ ///
+ /// 最大フロー問題 を解くライブラリです。
+ ///
+ /// 容量の型
+ /// に対応する演算を提要する型
+ ///
+ /// 制約: は int, long。
+ ///
+ /// 内部では各辺 e について 2 つの変数、流量 f_e と容量 c_e を管理しています。
+ /// 頂点 v から出る辺の集合を out(v)、入る辺の集合を in(v)、
+ /// また頂点 v について g(v, f) = (Σ_in(v) f_e) - (Σ_out(v) f_e) とします。
+ ///
+ ///
+ public class MFGraph
+ where TValue : struct
+ where TOp : struct, INumOperator
+ {
+ static readonly TOp op = default;
+
+ ///
+ /// 頂点 0 辺のグラフを作ります。
+ ///
+ ///
+ /// 制約: 0 ≤ ≤ 10^8
+ /// 計算量: O()
+ ///
+ public MFGraph(int n)
+ {
+ _n = n;
+ _g = new List[n];
+ for (int i = 0; i < n; i++)
+ {
+ _g[i] = new List();
+ }
+ _pos = new List<(int first, int second)>();
+ }
+
+ ///
+ /// から へ
+ /// 最大容量 、流量 0 の辺を追加し、何番目に追加された辺かを返します。
+ ///
+ ///
+ /// 制約:
+ ///
+ /// -
+ /// 0 ≤ , < n
+ ///
+ /// -
+ /// 0 ≤
+ ///
+ ///
+ /// 計算量: ならしO(1)
+ ///
+ public int AddEdge(int from, int to, TValue cap)
+ {
+ int m = _pos.Count;
+ Debug.Assert(0 <= from && from < _n);
+ Debug.Assert(0 <= to && to < _n);
+ Debug.Assert(op.LessThanOrEqual(default, cap));
+ _pos.Add((from, _g[from].Count));
+ _g[from].Add(new EdgeInternal(to, _g[to].Count, cap));
+ _g[to].Add(new EdgeInternal(from, _g[from].Count - 1, default));
+ return m;
+ }
+
+ ///
+ /// 今の内部の辺の状態を返します。
+ ///
+ ///
+ /// AddEdge で 番目 (0-indexed) に追加された辺を返す。
+ /// 計算量: O(1)
+ ///
+ public Edge GetEdge(int i)
+ {
+ int m = _pos.Count;
+ Debug.Assert(0 <= i && i < m);
+ var _e = _g[_pos[i].first][_pos[i].second];
+ var _re = _g[_e.To][_e.Rev];
+ return new Edge(_pos[i].first, _e.To, op.Add(_e.Cap, _re.Cap), _re.Cap);
+ }
+
+ ///
+ /// 今の内部の辺の状態を返します。
+ ///
+ ///
+ /// 辺の順番はadd_edgeで追加された順番と同一。
+ /// 計算量: O(n)
+ ///
+ public List Edges()
+ {
+ int m = _pos.Count;
+ var result = new List();
+ for (int i = 0; i < m; i++)
+ {
+ result.Add(GetEdge(i));
+ }
+ return result;
+ }
+
+ ///
+ /// 番目に追加された辺の容量、流量を
+ /// , に変更します。
+ ///
+ ///
+ ///
+ /// 他の辺の容量、流量は変更しません。
+ /// 辺 の流量、容量のみを
+ /// , へ変更します。
+ ///
+ ///
+ public void ChangeEdge(int i, TValue newCap, TValue newFlow)
+ {
+ int m = _pos.Count;
+ Debug.Assert(0 <= i && i < m);
+ Debug.Assert(op.LessThanOrEqual(default, newFlow) && op.LessThanOrEqual(newFlow, newCap));
+ var _e = _g[_pos[i].first][_pos[i].second];
+ var _re = _g[_e.To][_e.Rev];
+ _e.Cap = op.Subtract(newCap, newFlow);
+ _re.Cap = newFlow;
+ }
+
+ ///
+ /// 頂点 から へ流せる限り流し、
+ /// 流せた量を返します。
+ ///
+ ///
+ ///
+ /// 複数回呼ぶことも可能で、その時の挙動は
+ /// 変更前と変更後の流量を f_e, f'_e として、以下の条件を満たすように変更します。
+ ///
+ ///
+ /// -
+ /// 0 ≤ f'_e ≤ C_e
+ ///
+ /// -
+ ///
+ /// , 以外の頂天 v について、
+ /// g(v, f) = g(v, f')
+ ///
+ ///
+ /// -
+ ///
+ /// (flowLimit を指定した場合) g(t, f') - g(t, f) ≤ flowLimit
+ ///
+ ///
+ /// -
+ ///
+ /// g(t, f') - g(t, f) が委譲の条件を満たすうち最大。この g(t, f') - g(t, f) を返す。
+ ///
+ ///
+ ///
+ /// 制約: 返値が に収まる。
+ /// 計算量: m を追加された辺数として、
+ ///
+ /// -
+ /// O(min(n^(2/3) m, m^(3/2))) (辺の容量が全部 1 の時)
+ ///
+ /// -
+ /// O(n^2 m)
+ ///
+ ///
+ ///
+ public TValue Flow(int s, int t)
+ {
+ return Flow(s, t, op.MaxValue);
+ }
+
+ ///
+ /// 頂点 から へ
+ /// 流量 に達するまで流せる限り流し、
+ /// 流せた量を返します。
+ ///
+ ///
+ ///
+ /// 複数回呼ぶことも可能で、その時の挙動は
+ /// 変更前と変更後の流量を f_e, f'_e として、以下の条件を満たすように変更します。
+ ///
+ ///
+ /// -
+ /// 0 ≤ f'_e ≤ C_e
+ ///
+ /// -
+ ///
+ /// , 以外の頂天 v について、
+ /// g(v, f) = g(v, f')
+ ///
+ ///
+ /// -
+ ///
+ /// ( を指定した場合) g(t, f') - g(t, f) ≤
+ ///
+ ///
+ /// -
+ ///
+ /// g(t, f') - g(t, f) が委譲の条件を満たすうち最大。この g(t, f') - g(t, f) を返す。
+ ///
+ ///
+ ///
+ /// 制約: 返値が に収まる。
+ /// 計算量: m を追加された辺数として、
+ ///
+ /// -
+ /// O(min(n^(2/3) m, m^(3/2))) (辺の容量が全部 1 の時)
+ ///
+ /// -
+ /// O(n^2 m)
+ ///
+ ///
+ ///
+ public TValue Flow(int s, int t, TValue flowLimit)
+ {
+ Debug.Assert(0 <= s && s < _n);
+ Debug.Assert(0 <= t && t < _n);
+
+ var level = new int[_n];
+ var iter = new int[_n];
+ var que = new Queue();
+
+ void Bfs()
+ {
+ for (int i = 0; i < _n; i++)
+ {
+ level[i] = -1;
+ }
+
+ level[s] = 0;
+ que.Clear();
+ que.Enqueue(s);
+ while (que.Count > 0)
+ {
+ int v = que.Dequeue();
+ foreach (var e in _g[v])
+ {
+ if (op.Equals(e.Cap, default) || level[e.To] >= 0) continue;
+ level[e.To] = level[v] + 1;
+ if (e.To == t) return;
+ que.Enqueue(e.To);
+ }
+ }
+ }
+
+ TValue Dfs(int v, TValue up)
+ {
+ if (v == s) return up;
+ var res = default(TValue);
+ int level_v = level[v];
+ for (; iter[v] < _g[v].Count; iter[v]++)
+ {
+ EdgeInternal e = _g[v][iter[v]];
+ if (level_v <= level[e.To] || op.Equals(_g[e.To][e.Rev].Cap, default)) continue;
+ var up1 = op.Subtract(up, res);
+ var up2 = _g[e.To][e.Rev].Cap;
+ var d = Dfs(e.To, op.LessThan(up1, up2) ? up1 : up2);
+ if (op.Compare(d, default) <= 0) continue;
+ _g[v][iter[v]].Cap = op.Add(_g[v][iter[v]].Cap, d);
+ _g[e.To][e.Rev].Cap = op.Subtract(_g[e.To][e.Rev].Cap, d);
+ res = op.Add(res, d);
+ if (res.Equals(up)) break;
+ }
+
+ return res;
+ }
+
+ TValue flow = default;
+ while (op.LessThan(flow, flowLimit))
+ {
+ Bfs();
+ if (level[t] == -1) break;
+ for (int i = 0; i < _n; i++)
+ {
+ iter[i] = 0;
+ }
+ while (op.LessThan(flow, flowLimit))
+ {
+ var f = Dfs(t, op.Subtract(flowLimit, flow));
+ if (op.Equals(f, default)) break;
+ flow = op.Add(flow, f);
+ }
+ }
+ return flow;
+ }
+
+ ///
+ /// 長さ n の配列を返します。
+ /// i 番目の要素には、頂点 から i へ残余グラフで到達可能なとき、
+ /// またその時のみ true を返します。
+ /// Flow(s, t) を flowLimit なしでちょうど一回呼んだ後に呼ぶと、
+ /// 返り値はs, t 間の mincut に対応します。
+ ///
+ ///
+ ///
+ /// 各辺 e = (u, v, f_e, c_e) について、 f_e < c_e ならば辺 (u, v) を張り、
+ /// 0 < f_e ならば辺 (u, v) を張ったと仮定したとき、
+ /// 頂点 ss から到達可能な頂点の集合を返します。
+ ///
+ /// 計算量: m を追加された辺数として、
+ ///
+ /// -
+ /// O(n + m)
+ ///
+ ///
+ ///
+ public bool[] MinCut(int s)
+ {
+ var visited = new bool[_n];
+ var que = new Queue();
+ que.Enqueue(s);
+ while (que.Count > 0)
+ {
+ int p = que.Dequeue();
+ visited[p] = true;
+ foreach (var e in _g[p])
+ {
+ if (!op.Equals(e.Cap, default) && !visited[e.To])
+ {
+ visited[e.To] = true;
+ que.Enqueue(e.To);
+ }
+ }
+ }
+
+ return visited;
+ }
+
+ ///
+ /// フロー流すグラフの各辺に対応した情報を持ちます。
+ ///
+ public struct Edge
+ {
+ /// フローが流出する頂点。
+ public int From { get; set; }
+ /// フローが流入する頂点。
+ public int To { get; set; }
+ /// 辺の容量。
+ public TValue Cap { get; set; }
+ /// 辺の流量。
+ public TValue Flow { get; set; }
+ public Edge(int from, int to, TValue cap, TValue flow)
+ {
+ From = from;
+ To = to;
+ Cap = cap;
+ Flow = flow;
+ }
+ };
+
+ private class EdgeInternal
+ {
+ public int To { get; set; }
+ public int Rev { get; set; }
+ public TValue Cap { get; set; }
+ public EdgeInternal(int to, int rev, TValue cap)
+ {
+ To = to;
+ Rev = rev;
+ Cap = cap;
+ }
+ };
+
+ private readonly int _n;
+ private readonly List<(int first, int second)> _pos;
+ private readonly List[] _g;
+ }
+}