Problem:
Bipartite Matching
A bipartite graph
G = (V, E)
is a graph in which the vertex set
V
can be divided into two disjoint subsets
X
and
Y
such that every edge
e ∈ E
has one end point in
X
and the other end point in
Y
.
A matching
M
is a subset of edges such that each node in
V
appears in at most one edge in
M
.
Given a bipartite graph, find the size of the matching which has the largest size.
Input
|X|
|Y|
|E|
x
0
y
0
x
1
y
1
:
x
|E|-1
y
|E|-1
|X|
and
|Y|
are the number of vertices in
X
and
Y
respectively, and
|E|
is the number of edges in the graph
G
. The vertices in
X
are named with the numbers 0, 1,...,
|X|
-1, and vertices in
Y
are named with the numbers 0, 1,...,
|Y|
-1, respectively.
x
i
and
y
i
are the node numbers from
X
and
Y
respectevely which represent the end-points of the
i
-th edge.
Output
Print the largest size of the matching.
Constraints
1 ≤
|X|
,
|Y|
≤ 100
0 ≤
|E|
≤ 10,000
Sample Input 1
3 4 6
0 0
0 2
0 3
1 1
2 1
2 3
Sample Output 1
3

Submitted solutions:

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

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

template <typename T>
void print(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
    if (v.empty()) cout << '\n';
}

constexpr int MOD = 998244353;

template <bool directed = false>
struct Graph {
    using L = int;

    struct edge {
        int to, id;

        edge(int to, int id) : to(to), id(id) {}

        L get_len() const { return 1; }
    };

    vector<vector<edge>> es;
    const int n;
    int m;

    Graph(int n) : es(n), n(n), m(0) {}

    bool is_directed() { return directed; }

    inline const vector<edge> &operator[](int k) const { return es[k]; }

    inline vector<edge> &operator[](int k) { return es[k]; }

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m);
        if (!directed) es[to].emplace_back(from, m);
        m++;
    }

    void read(int m, int margin = -1) {
        for (int i = 0; i < m; i++) {
            int u, v;
            cin >> u >> v;
            add_edge(u + margin, v + margin);
        }
    }

    void read_rooted_tree(int margin = -1) {
        for (int i = 1; i < n; i++) {
            int p;
            cin >> p;
            add_edge(p + margin, i);
        }
    }
};

template <typename T, bool directed = false>
struct Weighted_Graph {
    using L = T;

    struct edge {
        int to, id;
        T len;

        edge(int to, int id, T len) : to(to), id(id), len(len) {}

        L get_len() const { return len; }
    };

    vector<vector<edge>> es;
    const int n;
    int m;

    Weighted_Graph(int n) : es(n), n(n), m(0) {}

    bool is_directed() { return directed; }

    inline const vector<edge> &operator[](int k) const { return es[k]; }

    inline vector<edge> &operator[](int k) { return es[k]; }

    void add_edge(int from, int to, T len) {
        es[from].emplace_back(to, m, len);
        if (!directed) es[to].emplace_back(from, m, len);
        m++;
    }

    void read(int m, int margin = -1) {
        for (int i = 0; i < m; i++) {
            int u, v;
            T w;
            cin >> u >> v >> w;
            add_edge(u + margin, v + margin, w);
        }
    }

    void read_rooted_tree(int margin = -1) {
        for (int i = 1; i < n; i++) {
            int p;
            T w;
            cin >> p >> w;
            add_edge(p + margin, i, w);
        }
    }
};

template <typename G>
struct BFS {
    vector<int> d;
    vector<int> pre_v, pre_e;
    const int s;

    BFS(const G &g, int s) : d(g.n, infty()), pre_v(g.n), pre_e(g.n), s(s) {
        queue<int> que;
        d[s] = 0;
        que.push(s);
        while (!que.empty()) {
            int i = que.front();
            que.pop();
            for (auto &e : g[i]) {
                if (d[i] + 1 < d[e.to]) {
                    d[e.to] = d[i] + 1;
                    pre_v[e.to] = i, pre_e[e.to] = e.id;
                    que.push(e.to);
                }
            }
        }
    }

    inline int infty() { return (1 << 30) - 1; }

    inline const int &operator[](int i) const { return d[i]; }

    // s-t 最短路の (頂点、辺)
    pair<vector<int>, vector<int>> shortest_path(int t) {
        if (d[t] == infty()) return make_pair(vector<int>{}, vector<int>{});
        vector<int> path_v, path_e;
        for (int now = t; now != s; now = pre_v[now]) {
            path_v.push_back(now);
            path_e.push_back(pre_e[now]);
        }
        path_v.push_back(s);
        reverse(begin(path_v), end(path_v));
        reverse(begin(path_e), end(path_e));
        return make_pair(path_v, path_e);
    }
};

struct Partition_Matroid {
    const int m; // |E|
    const int n; // 分割の個数
    vector<vector<int>> ids;
    vector<int> belong;
    vector<int> d, cnt;
    vector<vector<int>> used;

    Partition_Matroid(int m, int n, const vector<vector<int>> &ids, const vector<int> &d) : m(m), n(n), ids(ids), d(d), cnt(n), used(n) {
        assert((int)ids.size() == n && (int)d.size() == n);
        belong.assign(m, -1);
        for (int i = 0; i < n; i++) {
            for (auto &e : ids[i]) belong[e] = i;
        }
    }

    int size() { return m; }

    // X∈F 計算量 O(m+n)
    template <typename T>
    void set(const vector<T> &X) {
        fill(begin(cnt), end(cnt), 0);
        for (int i = 0; i < n; i++) used[i].clear();
        for (int i = 0; i < m; i++) {
            if (X[i]) cnt[belong[i]]++;
        }
        for (int i = 0; i < n; i++) {
            assert(cnt[i] <= d[i]);
            if (cnt[i] == d[i]) {
                for (auto &e : ids[i]) {
                    if (X[e]) used[i].push_back(e);
                }
            }
        }
    }

    // C(X,y) 計算量 O(max d_i)
    vector<int> circuit(int y) const {
        int p = belong[y];
        if (cnt[p] == d[p]) {
            vector<int> ret = used[p];
            ret.push_back(y);
            return ret;
        }
        return {};
    }
};

template <typename Matroid_1, typename Matroid_2>
pair<int, vector<int>> matroid_intersection(Matroid_1 M1, Matroid_2 M2) {
    assert(M1.size() == M2.size());
    const int m = M1.size();
    vector<bool> X(m, false);
    for (int i = 0;; i++) {
        M1.set(X), M2.set(X);
        Graph<true> G(m + 2);
        int s = m, t = m + 1;
        for (int y = 0; y < m; y++) {
            if (X[y]) continue;
            vector<int> c1 = M1.circuit(y), c2 = M2.circuit(y);
            if (c1.empty()) G.add_edge(s, y);
            for (auto &x : c1) {
                if (x != y) G.add_edge(x, y);
            }
            if (c2.empty()) G.add_edge(y, t);
            for (auto &x : c2) {
                if (x != y) G.add_edge(y, x);
            }
        }
        BFS B(G, s); // 最短路を求める
        vector<int> path = B.shortest_path(t).first;
        if (path.empty()) break;
        for (auto &e : path) {
            if (e != s && e != t) X[e] = !X[e];
        }
    }
    vector<int> ret;
    for (int i = 0; i < m; i++) {
        if (X[i]) ret.push_back(i);
    }
    return make_pair((int)ret.size(), ret);
};

int main() {
    int L, R, E;
    cin >> L >> R >> E;

    vector<vector<int>> idL(L), idR(R);

    for (int i = 0; i < E; i++) {
        int u, v;
        cin >> u >> v;
        idL[u].push_back(i);
        idR[v].push_back(i);
    }

    Partition_Matroid PL(E, L, idL, vector<int>(L, 1)), PR(E, R, idR, vector<int>(R, 1));

    cout << matroid_intersection(PL, PR).first << '\n';
}
accuracy: 1
time_ms: 70
memory_kb: 4808
final_rank: 7

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;

const ll INF = (ll)1e9;

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

// 変数を宣言して入力を代入する所の省略
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 = 100000;
struct edge{
    ll to, cap, rev;
};
vector<vector<edge>> G(Max_V);
vector<bool> used(Max_V, false);

void add_edge(ll from, ll to, ll cap){
    if(to < 0) return;
    G[from].push_back((edge){to, cap, (ll)G[to].size()});
    G[to].push_back((edge){from, 0, (ll)G[from].size() - 1});
}

ll find_aug_path_DFS(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(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(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;
    }
}

int main(void){
    llin(X, Y, E);
    ll s = X + Y, t = s + 1;
    rep(x, X){
        add_edge(s, x, 1);
    }
    rep(y, Y){
        add_edge(y + X, t, 1);
    }
    rep(i, E){
        ll x, y; cin >> x >> y;
        add_edge(x, y + X, 1);
    }
    cout << max_flow_FF(s, t) << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 6164
final_rank: 3

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

#ifdef LOCAL
#include "dbg.h"
#else
#define dbg(...) 14122021
#endif

template <typename T>
bool chmax(T &x, T y) {
    return x < y and (x = y, true);
}

template <typename T>
bool chmin(T &x, T y) {
    return x > y and (x = y, true);
}

struct flow_edge {
    int u;
    int v;
    int64_t cap;
    int64_t flow;

    explicit flow_edge(int u, int v, int64_t cap) : u(u), v(v), cap(cap), flow(0) {}
};

ostream &operator<<(ostream &os, const flow_edge &e) {
    return os << e.u << "->" << e.v << '(' << e.flow << '/' << e.cap << ')';
}

struct residual_edge {
    int u;
    int v;
    int64_t val;
    bool forward;

    explicit residual_edge(int u, int v, int64_t val, bool forward) : u(u), v(v), val(val), forward(forward) {}
};

ostream &operator<<(ostream &os, const residual_edge &e) {
    return os << e.u << "->" << e.v << '(' << e.val << ',' << (e.forward ? "F" : "B") << ')';
}

struct flow_graph {
    flow_graph(int);

    void addEdge(int, int, int64_t);
    void updateEdge(int, int, int64_t);
    void fordFulkerson(int, int);

    int V;
    vector<vector<flow_edge>> adj;
};

struct residual_graph {
    residual_graph(const flow_graph &);

    vector<residual_edge> findAugmentingPath(int, int);

    int V;
    vector<vector<residual_edge>> adj;
};

flow_graph::flow_graph(int V) : V(V) {
    adj.resize(V);
}

void flow_graph::addEdge(int u, int v, int64_t cap) {
    if (cap == 0) return;
    adj[u].emplace_back(u, v, cap);
}

void flow_graph::updateEdge(int u, int v, int64_t flow) {
    for (auto &e : adj[u]) {
        if (e.v == v) {
            e.flow += flow;
            return;
        }
    }
}

void flow_graph::fordFulkerson(int s, int t) {
    while (true) {
        residual_graph rg{*this};
        auto path = rg.findAugmentingPath(s, t);
        if (path.empty()) break;
        int64_t bottleneck = numeric_limits<int64_t>::max();
        for (auto &e : path) {
            chmin(bottleneck, e.val);
        }
        for (auto &e : path) {
            if (e.forward) {
                updateEdge(e.u, e.v, bottleneck);
            } else {
                updateEdge(e.v, e.u, -bottleneck);
            }
        }
    }
}

residual_graph::residual_graph(const flow_graph &fg) {
    V = fg.V;
    adj.resize(V);
    for (auto &x : fg.adj) {
        for (auto &e : x) {
            int u = e.u;
            int v = e.v;
            int64_t cap = e.cap;
            int64_t flow = e.flow;
            if (flow == 0) {
                adj[u].emplace_back(u, v, cap, true);
            } else if (flow == cap) {
                adj[v].emplace_back(v, u, cap, false);
            } else {
                adj[u].emplace_back(u, v, cap - flow, true);
                adj[v].emplace_back(v, u, flow, false);
            }
        }
    }
}

vector<residual_edge> residual_graph::findAugmentingPath(int s, int t) {
    vector<bool> vis(V);
    vector<residual_edge> path;
    vector<residual_edge> ret;
    auto dfs = [&](auto &&self, int u) -> void {
        vis[u] = true;
        if (u == t) {
            ret = path;
        }
        for (auto &e : adj[u]) {
            int v = e.v;
            if (!vis[v]) {
                path.push_back(e);
                self(self, v);
                path.pop_back();
            }
        }
    };
    dfs(dfs, s);
    return ret;
}

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

    int x, y, e;
    cin >> x >> y >> e;

    int n = x + y + 2;
    flow_graph fg(n);

    while (e--) {
        int u, v;
        cin >> u >> v;
        v += x;
        fg.addEdge(u, v, 1);
    }

    for (int i = 0; i < x; ++i) {
        fg.addEdge(n - 2, i, 1);
    }

    for (int i = x; i < n - 2; ++i) {
        fg.addEdge(i, n - 1, 1);
    }

    fg.fordFulkerson(n - 2, n - 1);

    int ans = 0;
    for (auto &e : fg.adj[n - 2]) {
        ans += e.flow;
    }

    cout << ans << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 4076
final_rank: 1

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

#define int ll

stack<int> dfs(vector<set<int>> &adj,map<pair<int,int>,int> &omomi,vector<bool> &used,stack<int> path,int now,int goal){
    stack<int> ans;
    if(used.at(now)) return ans;
    used.at(now) = true;
    path.push(now);

    if(now == goal) return path;

    for(int nxt:adj.at(now)){
        if( !used.at(nxt) && omomi[make_pair(now,nxt)] >= 1){
            stack<int> tem = dfs(adj,omomi,used,path,nxt,goal);
            if(!tem.empty()) return tem;
        }
    }

    return ans;
}

int maxFlow(vector<set<pair<int,int>>> aadj,int n,int s,int t){
    vector<set<int>> adj(n);
    map<pair<int,int>,int> omomi;

    for(int i = 0;i < n;i++){
        for(pair<int,int> p:aadj.at(i)){
            adj.at(i).insert(p.first);
            adj.at(p.first).insert(i);

            omomi[ make_pair(i,p.first) ] += p.second;
            omomi[ make_pair(p.first,i ) ] += 0;
        }
    }


    int ans = 0;

    while(true){
        vector<bool> used(n,0);
        stack<int> rpath = dfs(adj,omomi,used,stack<int>(),s,t);
        if(rpath.empty()) break;

        stack<int> tem;
        while(!rpath.empty()){
            tem.push(rpath.top());
            rpath.pop();
        }
        vector<int> path;
        while(!tem.empty()){
            path.push_back(tem.top());
            tem.pop();
        }

        int upd = omomi[ make_pair(path.at(0),path.at(1))];
        for(int i = 0;i < path.size() - 1;i++){
            upd = min(upd,omomi[make_pair( path.at(i) , path.at(i + 1))]);
        }

        for(int i = 0;i < path.size() - 1;i++){
            omomi[make_pair( path.at(i) , path.at(i + 1))] -= upd;
            omomi[make_pair(path.at(i + 1),path.at(i))] += upd;
        }

//        cout << "path" << endl;
//        for(int i:path) cout << i << " ";
//        cout << upd << endl;

        ans += upd;
    }

    return ans;
}

int maxMatching(int x,int y,int e,vector<pair<int,int>> edges){
    vector<set<pair<int,int>>> adj(x + y + 2);
    for(int i = 1;i <= x;i++){
        adj.at(0).insert(make_pair(i,1));
    }

    for(pair<int,int> p:edges){
        adj.at(p.first + 1).insert(make_pair(x + 1 + p.second,1));
    }

    for(int i = x + 1;i <= x + y;i++){
        adj.at(i).insert(make_pair(x+y+1,1));
    }

    return maxFlow(adj,x + y + 2,0, x + y + 1);
}

void main_() {
    int x,y,e;
    cin >> x >> y >> e;
    vector<pair<int,int>> edges;
    rep(i,e){
        int a,b;
        cin >> a >> b;
        edges.push_back(make_pair(a,b));
    }
    cout << maxMatching(x,y,e,edges) << endl;
}

signed main(){
    main_();
}
accuracy: 1
time_ms: 20
memory_kb: 7440
final_rank: 5

code_snippet: //Atcoderならこれ使わずにac-library使おうね

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
vector<vector<int>> G;
map<pair<int,int>,int> edges;
vector<int> visited; 
int X,Y,E,N;
// Ford-Fullkerson法 O(max_flow*E)
bool dfs(int now){
    if(now == N-1){
        return true;
    }
    for(int next : G[now]){
        if(visited[next])continue;
        if(edges[make_pair(now,next)]>0){
            visited[next]=true;
            if(dfs(next)){
                edges[make_pair(now,next)]--;
                edges[make_pair(next,now)]++;
                return true;
            }
        }
    }
    return false;
}

int main(){
    cin >> X >> Y >> E;
    N = X+Y+2;
    G.resize(N);
    for(int i = 1 ; i <= X ; i++){
        G[0].push_back(i);
        G[i].push_back(0);
        edges[make_pair(0,i)]=1;
        edges[make_pair(i,0)]=0;
    }
    for(int i = X+1 ; i <= X+Y ; i++){
        G[i].push_back(N-1);
        G[N-1].push_back(i);
        edges[make_pair(i,N-1)]=1;
        edges[make_pair(N-1,i)]=0;
    }
    for(int i = 0 ; i < E ; i++){
        int x,y;
        cin >> x >> y;
        x+=1;
        y+=1;
        y+=X;
        G[x].push_back(y);
        G[y].push_back(x);
        edges[make_pair(x,y)]=1;
        edges[make_pair(y,x)]=0;
    }
    bool b = true;
    int flow = -1;
    while(b){
        visited.assign(N,false);
        visited[0]=true;
        flow++;
        b = dfs(0);
    }
    cout << flow << endl;
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 4844
final_rank: 2

code_snippet: #include <iostream>
#include <cassert>
#include <queue>
using namespace std;

template <typename T>
struct Edge
{
    int to;
    T cap;
    int rev;
    int id;
};

template <typename T>
struct Flow
{
    int n;
    int source;
    int sink;
    T current_flow;
    std::vector<int> d;
    std::vector<int> nx;
    std::vector<std::vector<Edge<T>>> g;
    std::vector<std::pair<int, int>> epos;

    Flow() {}

    Flow(int n, int source, int sink) : n(n), source(source), sink(sink), current_flow(0)
    {
        d.resize(n);
        nx.resize(n);
        g.resize(n);
    }

    void add_edge(int from, int to, T cap)
    {
        epos.push_back(std::pair<int, int>(from, (int)g[from].size()));
        g[from].push_back((Edge<T>){to, cap, (int)g[to].size(), (int)epos.size() - 1});
        g[to].push_back((Edge<T>){from, 0, (int)g[from].size() - 1, -1});
    }

    T delete_edge(int id)
    {
        Edge<T> &e = g[epos[id].first][epos[id].second];
        Edge<T> &re = g[e.to][e.rev];
        int u = re.to, v = e.to;
        T delete_f = re.cap;
        e.cap = 0;
        re.cap = 0;
        T reverse_f = delete_f - add_flow(u, v, delete_f);
        add_flow(u, source, reverse_f);
        add_flow(sink, v, reverse_f);
        current_flow -= reverse_f;
        return current_flow;
    }

    void bfs(int s)
    {
        fill(d.begin(), d.end(), -1);
        std::queue<int> que;
        d[s] = 0;
        que.push(s);
        while (que.size())
        {
            int u = que.front();
            que.pop();
            for (Edge<T> &e : g[u])
            {
                int v = e.to;
                Edge<T> &re = g[v][e.rev];
                if (re.cap > 0 && d[v] == -1)
                {
                    d[v] = d[u] + 1;
                    que.push(v);
                }
            }
        }
    }

    T dfs(int u, int s, T f)
    {
        if (u == s)
        {
            return f;
        }
        for (int &i = nx[u]; i < (int)g[u].size(); i++)
        {
            Edge<T> &e = g[u][i];
            int v = e.to;
            if (d[v] >= d[u] || e.cap == 0)
            {
                continue;
            }
            if (f == -1)
            {
                f = e.cap;
            }
            else
            {
                f = std::min(f, e.cap);
            }
            T fl = dfs(v, s, f);
            if (fl > 0)
            {
                e.cap -= fl;
                g[e.to][e.rev].cap += fl;
                return fl;
            }
        }
        return 0;
    }

    T add_flow(int r, int s, T max_f)
    {
        T res = 0;
        while (true)
        {
            if (max_f == 0)
            {
                return res;
            }
            bfs(s);
            if (d[r] == -1)
            {
                return res;
            }
            for (int i = 0; i < n; i++)
            {
                nx[i] = 0;
            }
            for (T f; (f = dfs(r, s, max_f)) > 0;)
            {
                res += f;
                if (max_f != -1)
                {
                    max_f -= f;
                }
            }
        }
    }

    T max_flow(T max_f = -1)
    {
        if (max_f != -1)
        {
            max_f -= current_flow;
            if (max_f < 0)
            {
                current_flow -= add_flow(sink, source, -max_f);
                return current_flow;
            }
        }
        current_flow += add_flow(source, sink, max_f);
        return current_flow;
    }

    int get_flow(int id)
    {
        Edge<T> &e = g[epos[id].first][epos[id].second];
        Edge<T> &re = g[e.to][e.rev];
        return re.cap;
    }
};

struct BipartiteMatching
{
    int p;
    int q;
    Flow<int> g;

    BipartiteMatching() {}

    BipartiteMatching(int p, int q) : p(p), q(q)
    {
        Flow<int> _g(p + q + 2, p + q, p + q + 1);
        for (int i = 0; i < p; i++)
            _g.add_edge(p + q, i, 1);
        for (int i = p; i < p + q; i++)
            _g.add_edge(i, p + q + 1, 1);
        std::swap(g, _g);
    }

    void add_edge(int u, int v)
    {
        g.add_edge(u, v + p, 1);
    }

    int delete_edge(int id)
    {
        return g.delete_edge(id + p + q);
    }

    int max_matching()
    {
        return g.max_flow();
    }

    bool is_used(int id)
    {
        return g.get_flow(id + p + q);
    }
};

int main()
{
    int x, y, e;
    cin >> x >> y >> e;
    BipartiteMatching g(x, y);
    for (int i = 0; i < e; i++)
    {
        int u, v;
        cin >> u >> v;
        g.add_edge(u, v);
        g.max_matching();
    }
    cout << g.max_matching() << endl;
}
accuracy: 1
time_ms: 120
memory_kb: 3756
final_rank: 13

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;


int V, E;
vector<int> G[MAX_N];
bool used[MAX_N];
int match[MAX_N];

void add_edge(int from, int to) {
	G[from].push_back(to);
	G[to].push_back(from);
}


bool dfs(int v) {
	used[v] = true;
	for (int i = 0; i < G[v].size(); i++) {
		int u = G[v][i];
		int w = match[u];
		if (w < 0 || (!used[w] && dfs(w))) {
			match[v] = u;
			match[u] = v;
			return true;
		}
	}
	return false;
}

int bipartite_matching() {
	int res = 0;
	memset(match, -1, sizeof(match));
	for (int v = 0; v < V; v++) {
		if (match[v] < 0) {
			memset(used, 0, sizeof(used));
			if (dfs(v)) {
				res++;
			}
			
		}
	}
	return res;
}


int main() {

	int X, Y;
	cin >> X >> Y >> E;

	V = X + Y;

	for (int i = 0; i < E; i++) {
		int a, b;
		cin >> a >> b;
		add_edge(a, b + X);
	}

	cout << bipartite_matching() << endl;

	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 59908
final_rank: 11

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;


int V, E;
struct edge { int to, cap, rev; };
vector<edge> G[MAX_N];
bool used[MAX_N];

void add_edge(int from, int to, int cap) {
	edge e1;
	e1.to = to, e1.cap = cap; e1.rev = G[to].size();
	G[from].push_back(e1);
	edge e2;
	e2.to = from, e2.cap = 0; e2.rev = G[from].size() - 1;
	G[to].push_back(e2);
}

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

	return 0;
}

int max_flow(int s, int t) {
	int flow = 0;
	while (true) {
		memset(used, 0, sizeof(used));
		int d = dfs(s, t, INF);
		if (d > 0) {
			flow += d;
		}
		else {
			return flow;
		}
	}
}

int main() {

	int X, Y;
	cin >> X >> Y >> E;

	int s = X + Y;
	int t = X + Y + 1;


	for (int i = 0; i < E; i++) {
		int a, b;
		cin >> a >> b;
		add_edge(a, b + X, 1);
	}

	for (int i = 0; i < X; i++) {
		add_edge(s, i, 1);
	}
	for (int i = 0; i < Y; i++) {
		add_edge(X + i, t, 1);
	}

	cout << max_flow(s, t) << endl;



	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 52168
final_rank: 8

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;


int V;
vector<int> G[MAX_N];
int match[MAX_N];
bool used[MAX_N];

void add_edge(int u, int v) {
	G[u].push_back(v);
	G[v].push_back(u);
}

bool dfs(int v) {
	used[v] = true;
	for (int i = 0; i < G[v].size(); i++) {
		int u = G[v][i];
		int w = match[u];
		if (w < 0 || (!used[w] && dfs(w))) {
			match[v] = u;
			match[u] = v;
			return true;
		}
	}
	return false;
}

int bipartite_matching() {
	int res = 0;
	memset(match, -1, sizeof(match));
	for (int v = 0; v < V; v++) {
		if (match[v] < 0) {
			memset(used, 0, sizeof(used));
			if (dfs(v)) {
				res++;
			}
		}
	}
	return res;
}

int main() {

	int X, Y, E;
	cin >> X >> Y >> E;
	V = X + Y;
	for (int i = 0; i < E; i++) {
		int u, v;
		cin >> u >> v;
		add_edge(u, v + X);
	}

	cout << bipartite_matching() << endl;
	
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 59896
final_rank: 10

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;


struct edge { int to, cap, rev; };

vector<edge> G[MAX_N];
bool used[MAX_N];

void add_edge(int from, int to, int cap) {
	edge e1;
	e1.to = to; e1.cap = cap; e1.rev = G[to].size();
	G[from].push_back(e1);
	edge e2;
	e2.to = from; e2.cap = 0; e2.rev = G[from].size() - 1;
	G[to].push_back(e2);
}

// 増加パスをDFSで見つける
int dfs(int v, int t, int f) {
	if (v == t) {
		return f;
	}
	used[v] = true;

	for (int i = 0; i < G[v].size(); i++) {
		edge& e = G[v][i];
		if (!used[e.to] && e.cap > 0) {
			int d = dfs(e.to, t, min(e.cap, f));
			if (d > 0) {
				e.cap -= d;
				G[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}

int max_flow(int s, int t) {
	int flow = 0;
	while (true) {
		memset(used, 0, sizeof(used));
		int f = dfs(s, t, INF);
		if (f > 0) {
			flow += f;
		}
		else {
			return flow;
		}
	}
}


int main() {

	int X, Y;
	int E;
	cin >> X >> Y >> E;

	int s = X + Y;
	int t = s + 1;
	int V = t + 1;

	for (int i = 0; i < E; i++) {
		int u, v;
		cin >> u >> v;
		add_edge(u, v + X, 1);
	}

	for (int i = 0; i < X; i++) {
		add_edge(s, i, 1);
	}

	for (int i = 0; i < Y; i++) {
		add_edge(i + X, t, 1);
	}

	cout << max_flow(s, t) << endl;
	
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 52172
final_rank: 9

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// Bipartite Matching (Simple)
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_7_A&lang=ja
// 蟻本 p. 197


int V, E;
int X, Y;
vector<int> G[MAX_N];
int match[MAX_N];
bool used[MAX_N];

// uとvを結ぶ辺をグラフに追加する
void add_edge(int u, int v) {
	G[u].push_back(v);
	G[v].push_back(u);
}

// 増加パスをDFSで探す
bool dfs(int v) {
	used[v] = true;
	for (int i = 0; i < G[v].size(); i++) {
		int u = G[v][i];
		int w = match[u];
		if (w < 0 || !used[w] && dfs(w)) {
			match[v] = u;
			match[u] = v;
			return true;
		}
	}
	return false;
}

int bipartite_matching() {
	int res = 0;
	memset(match, -1, sizeof(match));
	for (int v = 0; v < V; v++) {
		if (match[v] < 0) {
			memset(used, 0, sizeof(used));
			if (dfs(v)) {
				res++;
			}
		}
	}
	return res;
}

int main() {

	cin >> X >> Y >> E;
	V = X + Y;

	for (int i = 0; i < E; i++) {
		int u, v;
		cin >> u >> v;
		add_edge(u, X + v);
	}

	cout << bipartite_matching() << endl;

	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 59908
final_rank: 11

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// Bipartite Matching (from Maximum flow)
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_A&lang=jp
// 蟻本 p. 195
// 始点0, 終点V-1

int V, E;
int X, Y;
struct edge { int to, cap, rev; };

vector<edge> G[MAX_N];
int level[MAX_N];
int iter[MAX_N];

// from からtoへ向かう容量capの辺をグラフに追加する
void add_edge(int from, int to, int cap) {
	edge e1 = { to, cap, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, G[from].size() - 1 };
	G[to].push_back(e2);

}

// sからの最短距離をBFSで計算する
void bfs(int s) {
	memset(level, -1, sizeof(level));
	queue<int> que;
	level[s] = 0;
	que.push(s);
	while (!que.empty()) {
		int v = que.front(); que.pop();
		for (int i = 0; i < G[v].size(); i++) {
			edge& e = G[v][i];
			if (e.cap > 0 && level[e.to] < 0) {
				level[e.to] = level[v] + 1;
				que.push(e.to);
			}
		}
	}
}


// 増加パスをDFSで探す
int dfs(int v, int t, int f) {
	if (v == t) {
		return f;
	}

	for (int &i = iter[v]; i < G[v].size(); i++) {
		edge& e = G[v][i];
		if (e.cap > 0 && level[v] < level[e.to]) {
			int d = dfs(e.to, t, min(f, e.cap));
			if (d > 0) {
				e.cap -= d;
				G[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}


// sからtへの最大流を求める
int max_flow(int s, int t) {
	int flow = 0;
	while (true) {
		bfs(s);
		if (level[t] < 0) {
			return flow;
		}
		memset(iter, 0, sizeof(iter));
		int f;
		while ((f = dfs(s, t, INF)) > 0) {
			flow += f;
		}
	}
}

int main() {
	
	cin >> X >> Y >> E;
	V = X + Y + 2;

	for (int i = 0; i < E; i++) {
		int u, v, c;
		cin >> u >> v;
		add_edge(u, v + X, 1);
	}
	
	for (int i = 0; i < X; i++) {
		add_edge(X + Y, i, 1);
	}

	for (int i = 0; i < Y; i++) {
		add_edge(X + i, X + Y + 1, 1);
	}

	cout << max_flow(X + Y, X + Y + 1) << endl;

	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 65752
final_rank: 14

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// Bipartite Matching (from Maximum flow)
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_A&lang=jp
// 蟻本 p. 195
// 始点0, 終点V-1

int V, E;
int X, Y;
struct edge { int to, cap, rev; };

vector<edge> G[MAX_N];
int level[MAX_N];
int iter[MAX_N];

// from からtoへ向かう容量capの辺をグラフに追加する
void add_edge(int from, int to, int cap) {
	edge e1 = { to, cap, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, G[from].size() - 1 };
	G[to].push_back(e2);

}

// sからの最短距離をBFSで計算する
void bfs(int s) {
	memset(level, -1, sizeof(level));
	queue<int> que;
	level[s] = 0;
	que.push(s);
	while (!que.empty()) {
		int v = que.front(); que.pop();
		for (int i = 0; i < G[v].size(); i++) {
			edge& e = G[v][i];
			if (e.cap > 0 && level[e.to] < 0) {
				level[e.to] = level[v] + 1;
				que.push(e.to);
			}
		}
	}
}


// 増加パスをDFSで探す
int dfs(int v, int t, int f) {
	if (v == t) {
		return f;
	}

	for (int &i = iter[v]; i < G[v].size(); i++) {
		edge& e = G[v][i];
		if (e.cap > 0 && level[v] < level[e.to]) {
			int d = dfs(e.to, t, min(f, e.cap));
			if (d > 0) {
				e.cap -= d;
				G[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}


// sからtへの最大流を求める
int max_flow(int s, int t) {
	int flow = 0;
	while (true) {
		bfs(s);
		if (level[t] < 0) {
			return flow;
		}
		memset(iter, 0, sizeof(iter));
		int f;
		while ((f = dfs(s, t, INF)) > 0) {
			flow += f;
		}
	}
}

int main() {
	
	cin >> X >> Y >> E;
	V = X + Y + 2;

	for (int i = 0; i < E; i++) {
		int u, v, c;
		cin >> u >> v;
		add_edge(u, v + X, 1);
	}
	
	for (int i = 0; i < X; i++) {
		add_edge(X + Y, i, 1);
	}

	for (int i = 0; i < Y; i++) {
		add_edge(i, X + Y + 1, 1);
	}

	cout << max_flow(X + Y, X + Y + 1) << endl;

	return 0;
}
accuracy: 0.05
time_ms: 10
memory_kb: 65608
final_rank: 15

code_snippet: #include <iostream>
#include <vector>
#include <queue>
#include <map>
#include <set>

using namespace std;

using Vertex = pair<char, int>;
using Graph = map<Vertex, map<Vertex, bool>>;
using Path = map<Vertex, Vertex>;

const Vertex S = {'S', 0}, T = {'T', 0};

Path f(const Graph& G)
{
	set<Vertex> D;
	Path P;
	queue<Vertex> Q;
	for (Q.push(S); !Q.empty(); Q.pop()) {
		auto v = Q.front();
		if (G.count(v) > 0) for (auto [w, c] : G.at(v)) if (c && D.count(w) == 0) {
			P[w] = v;
			if (w == T) continue;
			D.insert(w);
			Q.push(w);
		}
	}
	return P;
}

int main()
{
	int nX, nY, nE, x, y, r = 0;
	Graph G;
	for (cin >> nX >> nY >> nE; nE > 0; --nE) {
		cin >> x >> y;
		G[S][{'X', x}] = G[{'X', x}][{'Y', y}] = G[{'Y', y}][T] = true;
	}
	Path P = f(G);
	while (P.count(T) > 0) {
		++r;
		for (Vertex w = T, v = P[w]; w != S; w = v, v = P[w]) {
			G[v][w] = false;
			G[w][v] = true;
		}
		P.clear();
		P = f(G);
	}
	cout << r << endl;
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 4024
final_rank: 4

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

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

struct Bipartite_Matching {
    vector<vector<int>> es;
    vector<int> d, match;
    vector<bool> used, used2;
    const int n, m;

    Bipartite_Matching(int n, int m) : es(n), d(n), match(m), used(n), used2(n), n(n), m(m) {}

    void add_edge(int u, int v) { es[u].push_back(v); }

    void _bfs() {
        fill(begin(d), end(d), -1);
        queue<int> que;
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                que.push(i);
                d[i] = 0;
            }
        }
        while (!que.empty()) {
            int i = que.front();
            que.pop();
            for (auto &e : es[i]) {
                int j = match[e];
                if (j != -1 && d[j] == -1) {
                    que.push(j);
                    d[j] = d[i] + 1;
                }
            }
        }
    }

    bool _dfs(int now) {
        used2[now] = true;
        for (auto &e : es[now]) {
            int u = match[e];
            if (u == -1 || (!used2[u] && d[u] == d[now] + 1 && _dfs(u))) {
                match[e] = now, used[now] = true;
                return true;
            }
        }
        return false;
    }

    int bipartite_matching() { // 右側のiは左側のmatch[i]とマッチングする
        fill(begin(match), end(match), -1), fill(begin(used), end(used), false);
        int ret = 0;
        while (true) {
            _bfs();
            fill(begin(used2), end(used2), false);
            int flow = 0;
            for (int i = 0; i < n; i++) {
                if (!used[i] && _dfs(i)) flow++;
            }
            if (flow == 0) break;
            ret += flow;
        }
        return ret;
    }
};

struct Dulmage_Mendelsohn_Decomposition : Bipartite_Matching {
    using BM = Bipartite_Matching;
    vector<vector<int>> rs;
    vector<vector<int>> ids_l, ids_r; // 左側と右側のブロック
    vector<int> comp_l, comp_r;       // 属するブロックの番号
    vector<int> vs;

    Dulmage_Mendelsohn_Decomposition(int n, int m) : BM(n, m), rs(n), comp_l(n), comp_r(m) {}

    void _dfs(int now, int col) {
        if (comp_l[now] != n + 1) return;
        comp_l[now] = col;
        for (auto &e : this->es[now]) {
            int to = this->match[e];
            if (to != -1) _dfs(to, col);
        }
        if (col > 0) vs.push_back(now);
    }

    void _rdfs(int now, int col) {
        if (comp_l[now] != n + 1) return;
        comp_l[now] = col;
        for (auto &e : rs[now]) _rdfs(e, col);
    }

    void decompose() {
        this->bipartite_matching();
        for (int i = 0; i < n; i++) {
            for (auto &e : this->es[i]) {
                int to = this->match[e];
                if (to != -1) rs[to].push_back(i);
            }
        }
        fill(begin(comp_l), end(comp_l), n + 1);
        for (int i = 0; i < n; i++) {
            bool flag = true;
            for (auto &e : es[i]) {
                if (this->match[e] == -1) {
                    _rdfs(i, 0);
                    flag = false;
                } else if (this->match[e] == i) {
                    flag = false;
                }
            }
            if (flag) _dfs(i, -1);
        }
        for (int i = 0; i < n; i++) _dfs(i, 1);
        for (int i = 0; i < n; i++) {
            if (comp_l[i] > 0) comp_l[i] = n + 1;
        }
        reverse(begin(vs), end(vs));
        int cnt = 1;
        for (auto &e : vs) {
            if (comp_l[e] == n + 1) _rdfs(e, cnt++);
        }
        for (int i = 0; i < n; i++) {
            if (comp_l[i] == -1) comp_l[i] = cnt;
        }
        for (int i = 0; i < m; i++) {
            if (this->match[i] == -1) {
                comp_r[i] = 0;
            } else {
                comp_r[i] = comp_l[this->match[i]];
            }
        }
        ids_l.resize(cnt + 1), ids_r.resize(cnt + 1);
        for (int i = 0; i < m; i++) {
            if (this->match[i] == -1) ids_r[0].push_back(i);
        }
        vector<bool> used(n, false);
        for (int i = 0; i < m; i++) {
            int e = this->match[i];
            if (e != -1) {
                ids_l[comp_l[e]].push_back(e);
                ids_r[comp_r[i]].push_back(i);
                used[e] = true;
            }
        }
        for (int i = 0; i < n; i++) {
            if (!used[i]) ids_l[cnt].push_back(i);
        }
    }
};

template <bool directed = false>
struct Graph {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es;
    const int n;
    int m;

    vector<int> d;
    vector<int> pre_v, pre_e;

    Graph(int n) : es(n), n(n), m(0), d(n), pre_v(n), pre_e(n) {}

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m);
        if (!directed) es[to].emplace_back(from, m);
        m++;
    }

    int bfs(int s, int t = 0) {
        fill(begin(d), end(d), -1);
        queue<int> que;
        d[s] = 0;
        que.emplace(s);
        while (!que.empty()) {
            int i = que.front();
            que.pop();
            for (auto &e : es[i]) {
                if (d[e.to] == -1) {
                    d[e.to] = d[i] + 1;
                    pre_v[e.to] = i, pre_e[e.to] = e.id;
                    que.push(e.to);
                }
            }
        }
        return d[t];
    }

    vector<int> shortest_path(int s, int t, bool use_id = false) {
        if (bfs(s, t) == -1) return {};
        vector<int> ret;
        for (int now = t; now != s; now = pre_v[now]) ret.push_back(use_id ? pre_e[now] : now);
        if (!use_id) ret.push_back(s);
        reverse(begin(ret), end(ret));
        return ret;
    }
};

template <typename T, bool directed = false>
struct Weighted_Graph {
    struct edge {
        int to;
        T cost;
        int id;
        edge(int to, T cost, int id) : to(to), cost(cost), id(id) {}
    };

    vector<vector<edge>> es;
    const T INF_T = numeric_limits<T>::max() / 2;
    const int n;
    int m;

    vector<T> d;
    vector<int> pre_v;

    Weighted_Graph(int n) : es(n), n(n), m(0), d(n), pre_v(n) {}

    void add_edge(int from, int to, T cost) {
        es[from].emplace_back(to, cost, m);
        if (!directed) es[to].emplace_back(from, cost, m);
        m++;
    }

    bool bellman_ford(int s) { // sから到達可能な負閉路を検出
        fill(begin(d), end(d), INF_T);
        d[s] = 0;
        bool ret = false;
        for (int i = 0; i < 2 * n; i++) {
            for (int j = 0; j < n; j++) {
                for (auto &e : es[j]) {
                    if (d[j] == INF_T) continue;
                    if (d[j] + e.cost < d[e.to]) {
                        d[e.to] = d[j] + e.cost, pre_v[e.to] = j;
                        if (i >= n - 1) d[e.to] = -INF_T, ret = true;
                    }
                }
            }
        }
        return ret;
    }

    bool negative_loop() { //全ての負閉路を検出
        fill(begin(d), end(d), 0);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (auto &e : es[j]) {
                    if (d[j] + e.cost < d[e.to]) {
                        d[e.to] = d[j] + e.cost;
                        if (i == n - 1) return true;
                    }
                }
            }
        }
    }

    vector<int> shortest_path(int s, int t) {
        bellman_ford(s);
        if (abs(d[t]) == INF_T) return {};
        vector<int> ret;
        for (int now = t; now != s; now = pre_v[now]) ret.push_back(now);
        ret.push_back(s), reverse(begin(ret), end(ret));
        return ret;
    }
};

struct Partition_Matroid {
    const int m; // |E|
    const int n; // 分割の個数
    vector<vector<int>> ids;
    vector<int> belong;
    vector<int> d, cnt;
    vector<vector<int>> used;

    Partition_Matroid(int m, int n, const vector<vector<int>> &ids, vector<int> &d) : m(m), n(n), ids(ids), d(d), cnt(n), used(n) {
        assert(ids.size() == n && d.size() == n);
        belong.assign(m, -1);
        for (int i = 0; i < n; i++) {
            for (auto &e : ids[i]) belong[e] = i;
        }
    }

    int size() { return m; }

    template <typename T>
    void set(const vector<T> &X) { // X∈F 計算量 O(m+n)
        fill(begin(cnt), end(cnt), 0);
        for (int i = 0; i < n; i++) used[i].clear();
        for (int i = 0; i < m; i++) {
            if (X[i]) cnt[belong[i]]++;
        }
        for (int i = 0; i < n; i++) {
            assert(cnt[i] <= d[i]);
            if (cnt[i] == d[i]) {
                for (auto &e : ids[i]) {
                    if (X[e]) used[i].push_back(e);
                }
            }
        }
    }

    vector<int> circuit(int y) const { // C(X,y) 計算量 O(1)
        assert(0 <= y && y < m);
        int p = belong[y];
        assert(0 <= p && p < n);
        if (cnt[p] == d[p]) {
            vector<int> ret = used[p];
            ret.push_back(y);
            return ret;
        }
        return {};
    }
};

struct Graphic_Matroid {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    const int m; // |E|
    const int n; // |V|
    vector<vector<edge>> list;
    vector<pair<int, int>> es;
    vector<int> pre_v, pre_e;
    vector<int> root;
    vector<int> depth;

    Graphic_Matroid(int m, int n, const vector<pair<int, int>> &es) : m(m), n(n), es(es), pre_v(n), pre_e(n), root(n), depth(n) {
        list.resize(n);
        for (int i = 0; i < m; i++) {
            auto [u, v] = es[i];
            list[u].emplace_back(v, i);
            list[v].emplace_back(u, i);
        }
    }

    int size() { return m; }

    template <typename T>
    void set(const vector<T> &X) { // X∈F 計算量 O(m+n)
        fill(begin(pre_v), end(pre_v), -1);
        fill(begin(pre_e), end(pre_e), -1);
        fill(begin(root), end(root), -1);
        fill(begin(depth), end(depth), -1);
        for (int i = 0; i < n; i++) {
            if (root[i] != -1) continue;
            queue<int> que;
            que.push(i);
            depth[i] = 0;
            while (!que.empty()) {
                int j = que.front();
                que.pop();
                root[j] = i;
                for (auto &e : list[j]) {
                    assert(!X[e.id] || root[e.to] == -1 || e.id == pre_e[j]);
                    if (X[e.id] && root[e.to] == -1) {
                        pre_v[e.to] = j;
                        pre_e[e.to] = e.id;
                        depth[e.to] = depth[j] + 1;
                        que.emplace(e.to);
                    }
                }
            }
        }
    }

    vector<int> circuit(int y) const { // C(X,y) 計算量 O(n)
        auto [s, t] = es[y];
        if (root[s] != root[t]) return {};
        int r = root[s];
        vector<int> ret;
        while (s != t) {
            if (depth[s] > depth[t]) {
                ret.push_back(pre_e[s]);
                s = pre_v[s];
            } else {
                ret.push_back(pre_e[t]);
                t = pre_v[t];
            }
        }
        ret.push_back(y);
        return ret;
    }
};

struct Transversal_Matroid {
    const int m; // |L|
    const int n; // |R|
    vector<vector<int>> es;
    vector<bool> fixed;
    vector<vector<int>> res;

    Transversal_Matroid(int m, int n, const vector<vector<int>> &es) : m(m), n(n), es(es), fixed(n), res(m + n) {}

    int size() { return m; }

    template <typename T>
    void set(const vector<T> &X) { // X∈F 計算量 O(e√(m+n)+m+n+e)
        fill(begin(fixed), end(fixed), true);
        for (int i = 0; i < m + n; i++) res[i].clear();
        Dulmage_Mendelsohn_Decomposition DM(m, n);
        for (int i = 0; i < m; i++) {
            if (X[i]) {
                for (auto &e : es[i]) DM.add_edge(i, e);
            }
        }
        DM.decompose();
        assert(DM.ids_r.back().empty());
        for (auto &e : DM.ids_r[0]) fixed[e] = false;
        for (int i = 0; i < m; i++) {
            for (auto &e : es[i]) res[i].push_back(m + e);
        }
        for (int i = 0; i < (int)DM.ids_l.size() - 1; i++) {
            int k = DM.ids_l[i].size(), l = DM.ids_r[i].size();
            for (int j = 0; j < k; j++) {
                int u = DM.ids_l[i][j], v = DM.ids_r[i][l - k + j];
                res[m + v].push_back(u);
            }
        }
    }

    vector<int> circuit(int y) const { // C(X,y) 計算量 O(e+m+n)
        for (auto &e : es[y]) {
            if (!fixed[e]) return {};
        }
        vector<bool> used(m + n, false);
        used[y] = true;
        queue<int> que;
        que.push(y);
        while (!que.empty()) {
            int i = que.front();
            que.pop();
            for (auto &e : res[i]) {
                if (!used[e]) {
                    used[e] = true;
                    que.emplace(e);
                }
            }
        }
        vector<int> ret;
        for (int i = 0; i < m; i++) {
            if (used[i]) ret.push_back(i);
        }
        return ret;
    }
};

template <typename Matroid_1, typename Matroid_2>
int Matroid_Intersection(Matroid_1 M1, Matroid_2 M2) {
    assert(M1.size() == M2.size());
    const int m = M1.size();
    vector<bool> X(m, false);
    for (int i = 0;; i++) {
        M1.set(X), M2.set(X);
        Graph<true> G(m + 2); // 最短路を求める
        int s = m, t = m + 1;
        for (int y = 0; y < m; y++) {
            if (X[y]) continue;
            vector<int> c1 = M1.circuit(y), c2 = M2.circuit(y);
            if (c1.empty()) G.add_edge(s, y);
            for (auto &x : c1) {
                if (x != y) G.add_edge(x, y);
            }
            if (c2.empty()) G.add_edge(y, t);
            for (auto &x : c2) {
                if (x != y) G.add_edge(y, x);
            }
        }
        vector<int> path = G.shortest_path(s, t);
        if (path.empty()) return i;
        for (auto &e : path) {
            if (e != s && e != t) X[e] = !X[e];
        }
    }
    return -1;
};

template <typename Matroid_1, typename Matroid_2, typename T>
vector<T> Weighted_Matroid_Intersection(Matroid_1 M1, Matroid_2 M2, vector<T> w) {
    assert(M1.size() == M2.size());
    const int m = M1.size();
    for (int i = 0; i < m; i++) w[i] *= m + 1;
    vector<bool> X(m, false);
    vector<T> ret(m + 1, -1);
    ret[0] = 0;
    for (int i = 1; i <= m; i++) {
        M1.set(X), M2.set(X);
        Weighted_Graph<T, true> G(m + 2); // コスト最大のパスのうち通る辺数が最小のものを求める
        int s = m, t = m + 1;
        for (int y = 0; y < m; y++) {
            if (X[y]) continue;
            vector<int> c1 = M1.circuit(y), c2 = M2.circuit(y);
            if (c1.empty()) G.add_edge(s, y, 0);
            for (auto &x : c1) {
                if (x != y) G.add_edge(x, y, w[x] + 1);
            }
            if (c2.empty()) G.add_edge(y, t, -w[y]);
            for (auto &x : c2) {
                if (x != y) G.add_edge(y, x, -w[y] + 1);
            }
        }
        vector<int> path = G.shortest_path(s, t);
        if (path.empty()) break;
        for (auto &e : path) {
            if (e != s && e != t) X[e] = !X[e];
        }
        T sum = 0;
        for (int j = 0; j < m; j++) {
            if (X[j]) sum += w[j];
        }
        ret[i] = sum / (m + 1);
    }
    return ret;
}

template <typename T>
T floor_sqrt(T x) {
    T L = 0, R = x + 1;
    while (R - L > 1) {
        T M = (L + R) / 2;
        (M * M <= x ? L : R) = M;
    }
    return L;
}

int main() {
    int L, R, E;
    cin >> L >> R >> E;

    vector<vector<int>> ids1(L), ids2(R);
    vector<int> d1(L, 1), d2(R, 1);

    for (int i = 0; i < E; i++) {
        int u, v;
        cin >> u >> v;
        ids1[u].push_back(i), ids2[v].push_back(i);
    }

    Partition_Matroid M1(E, L, ids1, d1), M2(E, R, ids2, d2);

    cout << Matroid_Intersection(M1, M2) << '\n';
}
accuracy: 1
time_ms: 70
memory_kb: 4760
final_rank: 6