Problem:
D: Dangerous Tower
ICPC で良い成績を収めるには修行が欠かせない．うさぎは ICPC で勝ちたいので，今日も修行をすることにした．
今日の修行は，積み木を丁寧に積み上げることによって，決してミスタイピングをしないほどの手先の器用さを手に入れようというものである．せっかく積み木がたくさんあるので，高い塔を作ろう．
積み木は
N
個あり，
i
番目 (1 ≤
i
≤
N
) の積み木は 1 ×
A
i
×
B
i
の直方体の形をしている．長さ 1 の辺は奥行き方向に用いて，長さ
A
i
,
B
i
の辺を横方向と高さ方向に 1 つずつ割り当てることにした．積み木を積み上げるとき，上の段の積み木は下の段の積み木より横の長さが厳密に短くなければならない．積み木は好きな順番に使うことができ，また，使わない積み木があってもよい．このような制約下で，作ることが可能な最も高い塔を作りたい．
Input
N
A
1
B
1
...
A
N
B
N
1 ≤
N
≤ 1,000，1 ≤
A
i
，
B
i
≤ 1,000,000 を満たす．入力の値はすべて整数である．
Output
塔の高さの最大値を 1 行に出力せよ．
Sample Input 1
3
10 40
10 40
20 30
Sample Output 1
80
Sample Input 2
4
1 2
2 3
3 4
4 1
Sample Output 2
11

Submitted solutions:

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

constexpr int INF = 1e9;

using pii = pair<int, int>;

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

using edges = vector<edge>;
using graph = vector<edges>;

void add_edge(graph& g, int from, int to, int cap, int cost) {
    g[from].push_back(edge{to, cap, cost, (int)g[to].size()});
    g[to].push_back(edge{from, 0, -cost, (int)g[from].size()-1});
}

int min_cost_flow(graph& g, int s, int t, int f) {
    int res = 0;
    const int N = g.size();
    vector<int> h(N), dist(N), prevv(N), preve(N);
    while(f > 0) {
        priority_queue<pii, vector<pii>, greater<pii>> que;
        fill(dist.begin(), dist.end(), INF);
        dist[s] = 0;
        que.push(make_pair(0, s));
        while(!que.empty()) {
            pii p = que.top();
            int v = p.second;
            que.pop();
            if(dist[v] < p.first) {
                continue;
            }
            for(int i=0; i<g[v].size(); ++i) {
                edge& e = g[v][i];
                if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) {
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.push(make_pair(dist[e.to], e.to));
                }
            }
        }
        if(dist[t] == INF) {
            return -1;
        }
        for(int v=0; v<N; ++v) {
            h[v] += dist[v];
        }
        int d = f;
        for(int v=t; v!=s; v=prevv[v]) {
            d = min(d, g[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d * h[t];
        for(int v=t; v!=s; v=prevv[v]) {
            edge& e = g[prevv[v]][preve[v]];
            e.cap -= d;
            g[v][e.rev].cap += d;
        }
    }
    return res;
}

int main() {
    int N;
    cin >> N;
    vector<int> A(N), B(N);
    vector<int> v;
    for(int i=0; i<N; ++i) {
        cin >> A[i] >> B[i];
        v.push_back(A[i]);
        v.push_back(B[i]);
    }
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    graph g(N+v.size()+3);
    int source = N + v.size();
    int unuse = N + v.size() + 1;
    int sink = N + v.size() + 2;
    for(int i=0; i<N; ++i) {
        add_edge(g, source, i, 1, 0);
    }
    for(int i=0; i<N; ++i) {
        int a = lower_bound(v.begin(), v.end(), A[i]) - v.begin();
        add_edge(g, i, N+a, 1, -B[i]);
        int b = lower_bound(v.begin(), v.end(), B[i]) - v.begin();
        add_edge(g, i, N+b, 1, -A[i]);
        add_edge(g, i, unuse, 1, 0);
    }
    for(int i=0; i<v.size(); ++i) {
        add_edge(g, N+i, sink, 1, 0);
    }
    add_edge(g, unuse, sink, INF, 0);
    cout << -min_cost_flow(g, source, sink, N) << endl;
}
accuracy: 1
time_ms: 200
memory_kb: 3792
final_rank: 12

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
// #include<boost/multiprecision/cpp_int.hpp>

using namespace std;
// using namespace atcoder;
// using bint = boost::multiprecision::cpp_int;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using P = pair<ll,ll>;
using vi = vector<ll>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using ve = vector<vector<int>>;
using vb = vector<bool>;
using vvb = vector<vb>;
#define rep(i,n) for(ll i = 0;i < (ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define sz(c) ((ll)(c).size())
#define LB(A,x) (int)(lower_bound(A.begin(),A.end(),x)-A.begin())
#define UB(A,x) (int)(upper_bound(A.begin(),A.end(),x)-A.begin())
// #define MOD 1000000007
#define MOD 998244353
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){for(int i = 0;i < v.size();i++)os<<v[i]<<(i+1!=v.size()?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.first<<" "<<p.second;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}
template<typename T> inline bool chmax(T &a,T b){if(a < b){a = b;return true;}return false;}
template<typename T> inline bool chmin(T &a,T b){if(a > b){a = b;return true;}return false;}
ld dist(ld x1,ld y1,ld x2, ld y2){return sqrtl((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));}

// https://ei1333.github.io/library/graph/flow/primal-dual.hpp.html
template<typename flow_t,typename cost_t>
struct MinCostFlow{
  struct edge{
    int to;
    flow_t cap;cost_t cost;
    int rev;
    bool is_e;// 本当の辺かどうか
  };
  vector<vector<edge>> v;
  vector<cost_t> potential,mn_cost;
  vector<int> pre_v,pre_e;
  const cost_t inf;
  MinCostFlow(int n):v(n),inf(numeric_limits<cost_t>::max()){}
  void add_edge(int fr,int to,flow_t cap,cost_t cost){
    v[fr].emplace_back((edge){to,cap,cost,(int)v[to].size(),true});
    v[to].emplace_back((edge){fr,0,-cost,(int)v[fr].size()-1,false});
  }
  // s->tの流量fの最小コスト O(FElog(V))
  cost_t min_cost_flow(int s,int t,flow_t f){
    int n = (int)v.size();
    cost_t res = 0;
    using pi = pair<cost_t,int>;//{cost,頂点番号}
    priority_queue<pi,vector<pi>,greater<pi>> que;
    potential.assign(n,0);
    pre_v.assign(n,-1);pre_e.assign(n,-1);
    while(f > 0){
      mn_cost.assign(n,inf);
      que.emplace(0,s);
      mn_cost[s] = 0;
      while(!que.empty()){
        auto [o_cost,ov] = que.top();que.pop();
        if(mn_cost[ov] < o_cost)continue;
        for(int i = 0;i < (int)v[ov].size();i++){
          edge &e = v[ov][i];
          cost_t n_cost = mn_cost[ov] + e.cost + potential[ov]-potential[e.to];
          if(e.cap > 0 && n_cost < mn_cost[e.to]){
            mn_cost[e.to] = n_cost;
            pre_v[e.to] = ov,pre_e[e.to] = i;
            que.emplace(mn_cost[e.to],e.to);
          }
        }
      }
      if(mn_cost[t] == inf)return -1;
      for(int i = 0;i < n;i++)potential[i] += mn_cost[i];
      flow_t add_flow = f;
      for(int ov = t;ov != s;ov = pre_v[ov]){
        add_flow = min(add_flow,v[pre_v[ov]][pre_e[ov]].cap);
      }
      f -= add_flow;
      res += add_flow*potential[t];
      for(int ov = t;ov != s;ov = pre_v[ov]){
        edge &e = v[pre_v[ov]][pre_e[ov]];
        e.cap -= add_flow;
        v[ov][e.rev].cap += add_flow;
      }
    }
    return res;
  }
  // 復元の方法 下の実装で(ov->nv,辺の今の流量/辺の流量の最大値)
  void output(){
    for(int i = 0;i < v.size();i++){
      for(auto &e : v[i]){
        if(!e.is_e)continue;
        auto &rev_e = v[e.to][e.rev];
        cout << i << "->" << e.to << " (flow:" << rev_e.cap << "/" << rev_e.cap +e.cap << ")\n";
      }
    }
  }
};

int main(){
  
  ios_base::sync_with_stdio(0), cin.tie(0);
  int n;cin >> n;
  vi a(n),b(n);
  rep(i,n)cin >> a[i] >> b[i];
  vi cor;
  rep(i,n)cor.emplace_back(a[i]),cor.emplace_back(b[i]);
  sort(ALL(cor));
  cor.erase(unique(ALL(cor)),cor.end());
  MinCostFlow<ll,ll> mc(n+sz(cor)+2);
  int S = n + sz(cor),T = S+1;
  rep(i,n){
    int aid = n+LB(cor,a[i]),bid = n+LB(cor,b[i]);
    mc.add_edge(i,bid,1,-a[i]);
    mc.add_edge(i,aid,1,-b[i]);
  }
  rep(i,n)mc.add_edge(S,i,1,0);
  rep(i,sz(cor))mc.add_edge(n+i,T,1,0);
  mc.add_edge(S,T,n,0);
  cout << -mc.min_cost_flow(S,T,n) << "\n";
  

  return 0;
}
accuracy: 1
time_ms: 210
memory_kb: 4068
final_rank: 15

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
 * @brief template
 */

struct MaxFlow {
    struct Edge {
        int to, rev;
        ll cap;
    };
    int V;
    vector<vector<Edge>> G;
    vector<int> itr, level;
    using P = pair<int, int>;
    vector<P> es;

  public:
    MaxFlow() {}
    MaxFlow(int V) : V(V) {
        G.assign(V, vector<Edge>());
    }
    int add_vertex() {
        G.push_back(vector<Edge>());
        return V++;
    }
    void add_edge(int from, int to, ll cap) {
        int fid = SZ(G[from]), tid = SZ(G[to]);
        if (from == to)
            tid++;
        es.push_back({from, fid});
        G[from].push_back({to, tid, cap});
        G[to].push_back({from, fid, 0});
    }
    struct Type {
        int from, to;
        ll cap, recap;
    };
    Type get_edge(int i) {
        auto [from, pos] = es[i];
        auto e = G[from][pos];
        auto re = G[e.to][e.rev];
        return Type{from, e.to, e.cap, re.cap};
    }
    void bfs(int s) {
        level.assign(V, -1);
        queue<int> q;
        level[s] = 0;
        q.push(s);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto &e : G[v]) {
                if (e.cap > 0 && level[e.to] < 0) {
                    level[e.to] = level[v] + 1;
                    q.push(e.to);
                }
            }
        }
    }
    ll dfs(int v, int t, ll f) {
        if (v == t)
            return f;
        for (int &i = itr[v]; i < (int)G[v].size(); i++) {
            Edge &e = G[v][i];
            if (e.cap > 0 && level[v] < level[e.to]) {
                ll 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;
    }
    ll run(int s, int t) {
        ll ret = 0, f;
        while (bfs(s), level[t] >= 0) {
            itr.assign(V, 0);
            while ((f = dfs(s, t, INF)) > 0)
                ret += f;
        }
        return ret;
    }
    vector<int> cut() {
        vector<int> ret(V);
        rep(v, 0, V) if (level[v] < 0) ret[v] = 1;
        return ret;
    }
};

/**
 * @brief Maximum Flow
 */

template <class Cap, class Cost> struct MinCostFlow {
    struct X {
        int from, to;
        Cap lb, ub, flow;
        Cost cost;
    };
    struct Edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };
    using P = pair<int, int>;
    int n, m;
    vector<X> es;
    vector<Cap> exc;
    vector<Cost> dual;
    vector<vector<Edge>> g;
    Cost MX;
    MinCostFlow(int _n) : n(_n), m(0), exc(n), dual(n), g(n), MX(0) {}
    void add_edge(int from, int to, Cap lb, Cap ub, Cost cost) {
        m++;
        chmax(MX, cost);
        chmax(MX, -cost);
        es.push_back({from, to, lb, ub, 0, cost});
    }
    void add_excess(int v, Cap c) { exc[v] += c; }
    pair<bool, Cost> run() {
        MaxFlow mf(n + 2);
        int S = n, T = n + 1;
        Cap psum = 0, nsum = 0;
        for (auto &e : es) {
            exc[e.to] += e.lb;
            exc[e.from] -= e.lb;
            mf.add_edge(e.from, e.to, e.ub - e.lb);
        }
        rep(i, 0, n) {
            if (exc[i] > 0) {
                psum += exc[i];
                mf.add_edge(S, i, exc[i]);
            }
            if (exc[i] < 0) {
                nsum += -exc[i];
                mf.add_edge(i, T, -exc[i]);
            }
        }

        if (psum != nsum or mf.run(S, T) != psum)
            return {false, 0};

        using P = pair<int, int>;
        vector<P> pos;
        rep(i, 0, m) {
            auto e = mf.get_edge(i);
            Cost cost = es[i].cost * n;
            int fid = SZ(g[e.from]), tid = SZ(g[e.to]);
            if (e.from == e.to)
                tid++;
            pos.push_back({e.from, fid});
            g[e.from].push_back({e.to, tid, e.cap, cost});
            g[e.to].push_back({e.from, fid, e.recap, -cost});
        }

        // solve

        Cost eps = MX * n + 1;
        while (eps > 1) {
            eps = max<Cost>(eps >> 2, 1);
            refine(eps);
        }

        Cost ret = 0;
        rep(i, 0, m) {
            auto [from, fid] = pos[i];
            es[i].flow = es[i].ub - g[from][fid].cap;
            ret += es[i].flow * es[i].cost;
        }
        dual.assign(n, 0);
        for (;;) {
            bool upd = 0;
            rep(i, 0, n) {
                for (auto &e : g[i])
                    if (e.cap) {
                        auto cost = dual[i] + e.cost / n;
                        if (chmin(dual[e.to], cost)) {
                            upd = 1;
                        }
                    }
            }
            if (!upd)
                break;
        }
        return {true, ret};
    }
    Cap get_flow(int i) const { return es[i].flow; }

  private:
    void refine(Cost &eps) {
        exc.assign(n, 0);
        vector<int> used(n);
        queue<int> que;
        vector<int> iter(n);

        auto cost = [&](int from, const Edge &e) {
            return e.cost + dual[from] - dual[e.to];
        };
        auto push = [&](int from, Edge &e, Cap cap) {
            exc[from] -= cap;
            exc[e.to] += cap;
            g[e.to][e.rev].cap += cap;
            e.cap -= cap;
        };
        auto relabel = [&](int v) {
            iter[v] = 0;
            Cost down = MX * (n + 1);
            for (auto &e : g[v])
                if (e.cap) {
                    chmin(down, eps + cost(v, e));
                }
            dual[v] -= down;
            que.push(v);
            used[v] = 1;
        };

        rep(i, 0, n) {
            for (auto &e : g[i])
                if (e.cap and cost(i, e) < 0) {
                    push(i, e, e.cap);
                }
        }
        rep(i, 0, n) if (exc[i] > 0) {
            used[i] = 1;
            que.push(i);
        }
        while (!que.empty()) {
            auto v = que.front();
            que.pop();
            used[v] = 0;
            for (int &i = iter[v]; i < SZ(g[v]); i++) {
                auto &e = g[v][i];
                if (e.cap and cost(v, e) < 0) {
                    push(v, e, min(exc[v], e.cap));
                    if (!used[e.to] and exc[e.to] > 0) {
                        used[e.to] = 1;
                        que.push(e.to);
                    }
                    if (exc[v] == 0)
                        break;
                }
            }
            if (exc[v] > 0) {
                relabel(v);
            }
        }
        eps = 0;
        rep(i, 0, n) {
            for (auto &e : g[i])
                if (e.cap) {
                    chmax(eps, -cost(i, e));
                }
        }
    }
};

/**
 * @brief Minimum Cost b-flow
 */

int main() {
    int N;
    cin >> N;
    vector<ll> A(N), B(N), X;
    rep(i,0,N) cin >> A[i] >> B[i], X.push_back(A[i]), X.push_back(B[i]);
    UNIQUE(X);
    int L = X.size();
    MinCostFlow<ll,ll> G(N+L+2);
    G.add_edge(N+L+1,N+L,0,N,0);
    rep(i,0,N) G.add_edge(N+L,i,0,1,0);
    rep(i,0,L) G.add_edge(N+i,N+L+1,0,1,0);
    rep(i,0,N) {
        int AX = LB(X,A[i]), BX = LB(X,B[i]);
        G.add_edge(i,N+AX,0,1,-B[i]);
        G.add_edge(i,N+BX,0,1,-A[i]);
    }
    auto [b,ans] = G.run();
    cout << -ans << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 4312
final_rank: 1

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

using ll = long long;

#pragma GCC target("avx2")
#pragma GCC optmize("Ofast")

template <class ft = long long, class ct = long long>
struct PrimalDual {
  const ct INF;
  
  struct edge {
    int to;
    int rev;
    ft cap;
    ct cost;
  };
  
  int siz;
  vector<vector<edge>> graph;
  vector<ct> pot, minc;
  vector<int> prev_v, prev_id;
  
  PrimalDual(int n) : siz(n), INF(1e18), graph(n) {}
  
  void add_edge(int from, int to, ft cap, ct cost) {
    assert(from < siz && to < siz);
    graph[from].emplace_back((edge){to, (int)graph[to].size(), cap, cost});
    graph[to].emplace_back((edge){from, (int)graph[from].size() - 1, 0, -cost});
  }
  
  ct min_cost_flow(int s, int t, ft f) {
    ct ret = 0;
    using Pi = pair<ct, int>;
    priority_queue<Pi, vector<Pi>, greater<Pi> > pq;
    pot.assign(siz, 0);
    prev_v.assign(siz, -1);
    prev_id.assign(siz, -1);
    
    while (f > 0) {
      minc.assign(siz, INF);
      pq.emplace(0, s);
      minc[s] = 0;
      
      while (pq.size()) {
        auto [cost, v] = pq.top();
        pq.pop();
        
        if (minc[v] < cost) {
          continue;
        }
        for (int i = 0; i < graph[v].size(); ++i) {
          auto &e = graph[v][i];
          ct nc = minc[v] + e.cost + pot[v] - pot[e.to];
          if (e.cap > 0 && minc[e.to] > nc) {
            minc[e.to] = nc;
            prev_v[e.to] = v;
            prev_id[e.to] = i;
            pq.emplace(minc[e.to], e.to);
          }
        }
      }
      
      if (minc[t] == INF) {
        return -1;
      }
      
      for (int v = 0; v < siz; ++v) {
        pot[v] += minc[v];
      }
      ft addflow = f;
      for (int v = t; v != s; v = prev_v[v]) {
        addflow = min(addflow, graph[prev_v[v]][prev_id[v]].cap);
      }
      f -= addflow;
      ret += addflow * pot[t];
      for (int v = t; v != s; v = prev_v[v]) {
        auto &e = graph[prev_v[v]][prev_id[v]];
        e.cap -= addflow;
        graph[e.to][e.rev].cap += addflow;
      }
    }
    return ret;
  }
  
};

const ll ABMAX = int(1e6) + 1;

int main() {
  ll N; cin >> N;
  vector<ll> A(N), B(N);
  set<ll> zt;
  zt.insert(0);
  for (int i = 0; i < N; ++i) {
    cin >> A[i] >> B[i];
    zt.insert(A[i]);
    zt.insert(B[i]);
  }
  
  map< int, int > num_to_id;
  {
    int cnt = 0;
    for (auto v : zt) {
      num_to_id[v] = cnt;
      cnt++;
    }
  }
  int siz = zt.size();
  
  int S = siz + N, T = S + 1;
  PrimalDual mcf(T + 1);
  ll base = 0;
  ll n0 = num_to_id[0];
  for (int i = 0; i < N; ++i) {
    ll a = A[i], b = B[i];
    ll na = num_to_id[a], nb = num_to_id[b];
    ll v = siz + i;
    base += (a + b);
    mcf.add_edge(S, v, 1, 0);
    mcf.add_edge(v, n0, 1, a + b);
    mcf.add_edge(v, na, 1, a);
    mcf.add_edge(v, nb, 1, b);
  }
  for (auto [key, value] : num_to_id) {
    ll f = 1;
    if (key == 0) f = N;
    mcf.add_edge(value, T, f, 0);
  }
  
  ll cost = mcf.min_cost_flow(S, T, N);
  ll res = base - cost;
  cout << res << endl;
  
  return 0;
}
accuracy: 1
time_ms: 120
memory_kb: 4224
final_rank: 7

code_snippet: #include<bits/stdc++.h>

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    mcf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap, cost});
        g[to].push_back(_edge{from, from_id, 0, -cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{
            pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost,
        };
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result(m);
        for (int i = 0; i < m; i++) {
            result[i] = get_edge(i);
        }
        return result;
    }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);
        // variants (C = maxcost):
        // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
        // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge
        std::vector<Cost> dual(_n, 0), dist(_n);
        std::vector<int> pv(_n), pe(_n);
        std::vector<bool> vis(_n);
        auto dual_ref = [&]() {
            std::fill(dist.begin(), dist.end(),
                      std::numeric_limits<Cost>::max());
            std::fill(pv.begin(), pv.end(), -1);
            std::fill(pe.begin(), pe.end(), -1);
            std::fill(vis.begin(), vis.end(), false);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::priority_queue<Q> que;
            dist[s] = 0;
            que.push(Q{0, s});
            while (!que.empty()) {
                int v = que.top().to;
                que.pop();
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                // dist[v] = shortest(s, v) + dual[s] - dual[v]
                // dist[v] >= 0 (all reduced cost are positive)
                // dist[v] <= (n-1)C
                for (int i = 0; i < int(g[v].size()); i++) {
                    auto e = g[v][i];
                    if (vis[e.to] || !e.cap) continue;
                    // |-dual[e.to] + dual[v]| <= (n-1)C
                    // cost <= C - -(n-1)C + 0 = nC
                    Cost cost = e.cost - dual[e.to] + dual[v];
                    if (dist[e.to] - dist[v] > cost) {
                        dist[e.to] = dist[v] + cost;
                        pv[e.to] = v;
                        pe[e.to] = i;
                        que.push(Q{dist[e.to], e.to});
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                // dual[v] = dual[v] - dist[t] + dist[v]
                //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) + (shortest(s, v) + dual[s] - dual[v])
                //         = - shortest(s, t) + dual[t] + shortest(s, v)
                //         = shortest(s, v) - shortest(s, t) >= 0 - (n-1)C
                dual[v] -= dist[t] - dist[v];
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result;
        result.push_back({flow, cost});
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = pv[v]) {
                c = std::min(c, g[pv[v]][pe[v]].cap);
            }
            for (int v = t; v != s; v = pv[v]) {
                auto& e = g[pv[v]][pe[v]];
                e.cap -= c;
                g[v][e.rev].cap += c;
            }
            Cost d = -dual[s];
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }

  private:
    int _n;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

using namespace atcoder;
using namespace std;
using ll=long long;
using vll=vector<ll>;
#define rep(i,n) for(ll i=0;i<ll(n);i++)

int main(){
    ll N;
    cin>>N;
    map<ll,ll> M;
    vll A(N),B(N);
    rep(i,N){
        cin>>A[i]>>B[i];
        M[A[i]]=M[B[i]]=1;
    }
    ll cnt=N;
    for(auto m:M)M[m.first]=cnt,cnt++;
    ll S=cnt;
    ll T=cnt+1;
    mcf_graph<ll,ll> G(cnt+2);
    rep(i,N)G.add_edge(S,i,1,0);
    ll d=1e9;
    rep(i,N){
        G.add_edge(i,M[A[i]],1,d-B[i]);
        G.add_edge(i,M[B[i]],1,d-A[i]);
    }
    
    for(auto m:M){
        G.add_edge(m.second,T,1,0);
    }

    auto V=G.slope(S,T);
    ll VN=V.size();
    ll an=0;
    rep(i,VN-1){
        ll rang=(V[i+1].second-V[i].second)/(V[i+1].first-V[i].first);
        for(ll j=V[i].first;j<=V[i+1].first;j++){
            ll res=V[i].second+rang*(j-V[i].first);
            an=max(an,d*j-res);
        }
    }
    cout<<an<<endl;

    
}
accuracy: 1
time_ms: 40
memory_kb: 4128
final_rank: 4

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;


template<class T = ll>
struct MCF {
private:
    const T inf = 1e18;
    vector<T> dist;       //最短距離(費用)
    vector<int> pv, pe;     //直前の頂点、辺    

public:
    struct edge {
        int to, rev; long long cap; T cost;
        edge(int t = 0, int r = 0, long long ca = 0, T co = 0)
            : to(t), rev(r), cap(ca), cost(co) {}
    };

    struct pii {
        T d; int v; //最短距離(費用)、頂点番号
        pii(T d_ = 0, int v_ = 0) : d(d_), v(v_) {}

        bool operator<(const pii& other) const {
            if (d != other.d) return d < other.d;
            return v < other.v;
        }

        bool operator>(const pii& other) const {
            if (d != other.d) return d > other.d;
            return v > other.v;
        }
    };

    int n;
    vector<vector<edge>> to;
    vector<T> pot;        //ポテンシャル

    MCF(int n_ = 1) : n(n_), to(n_), pot(n_), dist(n_), pv(n_), pe(n_) {}

    void addedge(int u, int v, long long cap, T cost) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap, cost });
        to[v].push_back({ u, su, 0, -cost });
    }

    //s->tへ流量fの最小費用流、流せないときは-1
    T mincostflow(int s, int t, long long f) {
        T res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return res; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;
            if (nf * (pot[t] - pot[s]) > 0) return res;

            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return res;
    }

    //s->tへ流量fの最小費用流、流せないときは費用と流せなかった流量のペア
    pair<T, long long> mincostflow_pair(int s, int t, long long f) {
        T res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return { res, f }; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;            
            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return { res, f };
    }
};

int main()
{
    int n;
    cin >> n;
    vi<int> a(n), b(n);
    rep(i, n) cin >> a[i] >> b[i];

    map<int, int> mp;
    rep(i, n) mp[a[i]];
    rep(i, n) mp[b[i]];
    int idx = 0;
    for (auto& elem : mp) elem.second = idx++;

    int sz = (int)mp.size();

    MCF mcf(n + sz + 2);
    int S = n + sz, T = S + 1;

    rep(i, n) mcf.addedge(S, i, 1, 0);
    rep(j, sz) mcf.addedge(j + n, T, 1, 0);

    rep(i, n) {
        mcf.addedge(i, mp[a[i]] + n, 1, -b[i]);
        mcf.addedge(i, mp[b[i]] + n, 1, -a[i]);
    }

    auto ans = mcf.mincostflow(S, T, n);
    cout << -ans << endl;

    return 0;
}
accuracy: 1
time_ms: 170
memory_kb: 4028
final_rank: 8

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;


template<class T = ll>
struct MCF {
private:
    const T inf = 1e18;
    vector<T> dist;       //最短距離(費用)
    vector<int> pv, pe;     //直前の頂点、辺    

public:
    struct edge {
        int to, rev; long long cap; T cost;
        edge(int t = 0, int r = 0, long long ca = 0, T co = 0)
            : to(t), rev(r), cap(ca), cost(co) {}
    };

    struct pii {
        T d; int v; //最短距離(費用)、頂点番号
        pii(T d_ = 0, int v_ = 0) : d(d_), v(v_) {}

        bool operator<(const pii& other) const {
            if (d != other.d) return d < other.d;
            return v < other.v;
        }

        bool operator>(const pii& other) const {
            if (d != other.d) return d > other.d;
            return v > other.v;
        }
    };

    int n;
    vector<vector<edge>> to;
    vector<T> pot;        //ポテンシャル

    MCF(int n_ = 1) : n(n_), to(n_), pot(n_), dist(n_), pv(n_), pe(n_) {}

    void addedge(int u, int v, long long cap, T cost) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap, cost });
        to[v].push_back({ u, su, 0, -cost });
    }

    //s->tへ流量fの最小費用流、流せないときは-1
    T mincostflow(int s, int t, long long f) {
        T res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return -1; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;
            if (nf * (pot[t] - pot[s]) > 0) return res;

            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return res;
    }

    //s->tへ流量fの最小費用流、流せないときは費用と流せなかった流量のペア
    pair<T, long long> mincostflow_pair(int s, int t, long long f) {
        T res = 0;
        pot.assign(n, 0);
        while (f) {
            priority_queue<pii, vector<pii>, greater<pii>> pq;
            dist.assign(n, inf);
            dist[s] = 0;
            pq.push({ 0, s });
            while (!pq.empty()) {
                pii elem = pq.top();
                pq.pop();
                if (dist[elem.v] < elem.d) continue;
                int sv = (int)to[elem.v].size();
                for (int i = 0; i < sv; i++) {
                    edge& nv = to[elem.v][i];
                    if (nv.cap == 0 || dist[nv.to] + pot[nv.to] <= dist[elem.v] + pot[elem.v] + nv.cost) continue;
                    dist[nv.to] = dist[elem.v] + pot[elem.v] - pot[nv.to] + nv.cost;
                    pv[nv.to] = elem.v;
                    pe[nv.to] = i;
                    pq.push({ dist[nv.to], nv.to });
                }
            }

            if (dist[t] == inf) return { res, f }; //流せる最小費用流が存在しない

            for (int v = 0; v < n; v++) pot[v] += dist[v];

            long long nf = f;
            for (int v = t; v != s; v = pv[v]) nf = min(nf, to[pv[v]][pe[v]].cap);
            f -= nf;            
            res += nf * (pot[t] - pot[s]); //pot[s] = 0で不要だがポテンシャルを明確化する
            for (int v = t; v != s; v = pv[v]) {
                edge& nv = to[pv[v]][pe[v]];
                nv.cap -= nf;
                to[v][nv.rev].cap += nf;
            }
        }
        return { res, f };
    }
};

int main()
{
    int n;
    cin >> n;
    vi<int> a(n), b(n);
    rep(i, n) cin >> a[i] >> b[i];

    map<int, int> mp;
    rep(i, n) mp[a[i]];
    rep(i, n) mp[b[i]];
    int idx = 0;
    for (auto& elem : mp) elem.second = idx++;

    int sz = (int)mp.size();

    MCF mcf(n + sz + 2);
    int S = n + sz, T = S + 1;

    const ll inf = 1e18;
    rep(i, n) mcf.addedge(S, i, 1, 0);
    rep(j, sz) mcf.addedge(j + n, T, 1, 0);

    rep(i, n) {
        mcf.addedge(i, mp[a[i]] + n, 1, -b[i]);
        mcf.addedge(i, mp[b[i]] + n, 1, -a[i]);
    }

    auto ans = mcf.mincostflow(S, T, n);
    cout << -ans << endl;

    return 0;
}
accuracy: 0.16666666666666666
time_ms: 170
memory_kb: 3888
final_rank: 19

code_snippet: #include <bits/stdc++.h>




namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa


namespace zawa {

void SetFastIO() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
}

void SetPrecision(u32 dig) {
    std::cout << std::fixed << std::setprecision(dig);
}

} // namespace zawa




namespace zawa {

class U32Pair {
private:
    static constexpr u32 SHIFT{32};
    static constexpr u32 MASK{static_cast<u32>((1LL << SHIFT) - 1)};
    u64 value_{};
public:
    constexpr U32Pair() {}
    constexpr U32Pair(u32 first, u32 second) {
        value_ = (static_cast<u64>(first) << SHIFT) | second;
    }
    constexpr u32 first() const noexcept {
        return static_cast<u32>(value_ >> SHIFT);
    }
    constexpr u32 second() const noexcept {
        return static_cast<u32>(value_ & MASK);
    }
    constexpr u64 combined() const noexcept {
        return value_;
    }
    constexpr U32Pair& operator=(const U32Pair& rhs) {
        value_ = rhs.value_;
        return *this;
    }
    friend constexpr bool operator==(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ == rhs.value_;
    }
    friend constexpr bool operator!=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ != rhs.value_;
    }
    friend constexpr bool operator<(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ < rhs.value_;
    }
    friend constexpr bool operator<=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ <= rhs.value_;
    }
    friend constexpr bool operator>(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ > rhs.value_;
    }
    friend constexpr bool operator>=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ >= rhs.value_;
    }
    friend std::ostream& operator<<(std::ostream& os, const U32Pair& pair) {
        os << '(' << pair.first() << ',' << pair.second() << ')';
        return os;
    }
};

struct U32PairHash {
    usize operator()(const U32Pair& pair) const noexcept {
        return std::hash<u64>{}(pair.combined());
    }
};

} // namespace zawa

#include <type_traits>

namespace zawa {

template <class Cap, class Cost>
class SuccessiveShortestPath {
public:
    static_assert(std::is_signed_v<Cost>, U"Cost must be signed");
    static constexpr Cost invalid{(std::numeric_limits<Cost>::max() >> 1) - 1};
    static constexpr u32 reverseId(u32 i) noexcept {
        return i ^ 1;
    }

    struct Edge {
        u32 from{}, to{};
        Cap residual{};
        Cost cost{};
        Edge() = default;
        Edge(u32 from, u32 to, const Cap& cap, const Cost& cost)
            : from{from}, to{to}, residual{cap}, cost{cost} {}
    };

    usize n_{}, m_{};
    std::vector<Edge> edges_;
    std::vector<std::vector<u32>> g_;
    std::vector<Cost> dist_, potential_;
    std::vector<U32Pair> prev_;
    Cost mcf{};

    constexpr usize size() const noexcept {
        return n_;
    }
    constexpr usize edgeSize() const noexcept {
        return m_;
    }
    
    SuccessiveShortestPath() = default;
    SuccessiveShortestPath(usize n, usize m = usize{}) 
        : n_{n}, m_{}, edges_{}, g_(n), dist_(n), potential_(n), prev_(n), mcf{} {
        g_.shrink_to_fit();
        dist_.shrink_to_fit();
        potential_.shrink_to_fit();
        prev_.shrink_to_fit();
        edges_.reserve(2 * m);
    }

    void emplace(u32 from, u32 to, const Cap& cap, const Cost& cost) {
        g_[from].emplace_back(m_);
        edges_.emplace_back(from, to, cap, cost);
        m_++;
    }

    u32 addEdge(u32 from, u32 to, const Cap& cap, const Cost& cost) {
        assert(from < size());
        assert(to < size());
        u32 res{static_cast<u32>(m_)};
        emplace(from, to, cap, cost);
        emplace(to, from, Cap{}, -cost);
        return res;
    }

    inline u32 from(u32 i) const noexcept {
        return edges_[i].from;
    }
    inline u32 to(u32 i) const noexcept {
        return edges_[i].to;
    }
    inline const Cap& residual(u32 i) const noexcept {
        return edges_[i].residual;
    }
    inline const Cost& cost(u32 i) const noexcept {
        return edges_[i].cost;
    }
    inline const Cap& flowed(u32 i) const noexcept {
        assert(i < edgeSize());
        return residual(i ^ 1);
    }
    inline const Cap& capcacity(u32 i) const noexcept {
        assert(i < edgeSize());
        return residual(i) + flowed(i);
    }

    inline Cost edgeCost(u32 i) const noexcept {
        return potential_[from(i)] + cost(i) - potential_[to(i)];
    }
    bool relax(u32 i) {
        if (residual(i) > 0 and dist_[to(i)] > dist_[from(i)] + edgeCost(i)) {
            dist_[to(i)] = dist_[from(i)] + edgeCost(i);
            prev_[to(i)] = U32Pair{from(i), i};
            return true;
        }
        return false;
    }

    bool dijkstra(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        using qt = std::pair<Cost, u32>;
        std::priority_queue<qt, std::vector<qt>, std::greater<qt>> que;
        que.emplace(dist_[s], s);
        while (que.size()) {
            auto [d, v]{que.top()};
            que.pop();
            if (dist_[v] < d) continue;
            for (u32 i : g_[v]) {
                if (relax(i)) {
                    que.emplace(dist_[to(i)], to(i));
                }
            }
        }
        return dist_[t] < invalid;
    }

    bool dagdp(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        std::vector<u32> ord;
        ord.reserve(size());
        std::vector<bool> vis(size());
        vis[s] = true;
        ord.push_back(s);
        for (u32 i{} ; i < ord.size() ; i++) {
            u32 v{ord[i]};
            for (auto e : g_[v]) {
                if (!vis[to(e)]) {
                    ord.push_back(to(e));
                    vis[to(e)] = true;
                }
                relax(e);
            }
        }
        return dist_[t] < invalid;
    }

    bool bellmanford(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        for (u32 i{} ; i + 1 < size() ; i++) {
            for (u32 j{} ; j < edgeSize() ; j++) {
                relax(j);
            }
        }
        return dist_[t] < invalid;
    }


    Cap flush(u32 s, u32 t, Cap up = std::numeric_limits<Cap>::max()) {
        for (u32 v{t} ; v != s ; v = prev_[v].first()) {
            up = std::min(up, residual(prev_[v].second()));
        }
        for (u32 v{t} ; v != s ; v = prev_[v].first()) {
            edges_[prev_[v].second()].residual -= up;
            edges_[prev_[v].second() ^ 1].residual += up;
        }
        return up;
    }

    bool flow(u32 s, u32 t, Cap flow) {
        assert(s < size());
        assert(t < size());
        while (flow > 0 and dijkstra(s, t)) {
            for (u32 i{} ; i < size() ; i++) {
                potential_[i] = potential_[i] + (dist_[i] == invalid ? Cost{} : dist_[i]);
            }
            Cap water{flush(s, t, flow)};
            mcf += potential_[t] * water;
            flow -= water;
        }
        return flow == 0;
    }

    Cap maxflow(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        Cap flow{};
        while (dijkstra(s, t)) {
            for (u32 i{} ; i < size() ; i++) {
                potential_[i] = potential_[i] + (dist_[i] == invalid ? Cost{} : dist_[i]);
            }
            Cap water{flush(s, t)};
            mcf += potential_[t] * water;
            flow += water;
        }
        return flow;
    }

    struct Line {
        Cap dn{}, up{};
        Cost slope{};
        Line() = default;
        Line(Cap dn, Cap up, Cost slope) : dn{dn}, up{up}, slope{slope} {}
    };
    std::vector<Line> slope(u32 s, u32 t) {
        assert(s < size());
        assert(t < size()); 
        Cap flow{};
        std::vector<Line> res;
        while (dijkstra(s, t)) {
            for (u32 i{} ; i < size() ; i++) {
                potential_[i] = potential_[i] + (dist_[i] == invalid ? Cost{} : dist_[i]);
            }
            Cap water{flush(s, t)};
            mcf += potential_[t] * water;
            res.emplace_back(flow, flow + water, potential_[t]);
            flow += water;
        }
        return res;
    }

    Cost minCost() const noexcept {
        return mcf;
    }
};

} // namespace zawa



namespace zawa {

template <class T>
class CompressedSequence {
private:
    std::vector<T> comped_;
    std::vector<u32> f_;
    
public:
    CompressedSequence() = default;
    CompressedSequence(const std::vector<T>& A) : comped_(A), f_(A.size()) {
        std::sort(comped_.begin(), comped_.end());
        comped_.erase(std::unique(comped_.begin(), comped_.end()), comped_.end());
        comped_.shrink_to_fit();
        f_.shrink_to_fit();
        for (u32 i{} ; i < A.size() ; i++) {
            f_[i] = std::lower_bound(comped_.begin(), comped_.end(), A[i]) - comped_.begin();
        }
    }     

    inline usize size() const noexcept {
        return comped_.size();
    }

    u32 operator[](const T& v) const {
        return std::lower_bound(comped_.begin(), comped_.end(), v) - comped_.begin();
    }

    inline u32 map(u32 i) const noexcept {
        assert(i < f_.size());
        return f_[i];
    }

    inline T inverse(u32 i) const noexcept {
        assert(i < size());
        return comped_[i];
    }
};

} // namespace zawa
using namespace zawa;

int main() {
    SetFastIO();
    
    int n; std::cin >> n;
    std::vector<int> a(n), b(n);
    std::vector<int> app(2 * n);
    for (int i{} ; i < n ; i++) {
        std::cin >> a[i] >> b[i];
        app[2 * i] = a[i];
        app[2 * i + 1] = b[i];
    }
    CompressedSequence<int> comp(app);
    int m{(int)comp.size()};
    SuccessiveShortestPath<int, long long> mcf(n + m + 2);
    int source{n + m}, sink{source + 1};
    for (int i{} ; i < n ; i++) {
        mcf.addEdge(source, i, 1, 0);
    }
    for (int i{} ; i < m ; i++) {
        mcf.addEdge(n + i, sink, 1, comp.inverse(i));
    }
    for (int i{} ; i < n ; i++) {
        mcf.addEdge(i, n + comp[a[i]], 1, 0);
        mcf.addEdge(i, n + comp[b[i]], 1, 0);
        mcf.addEdge(i, sink, 1, a[i] + b[i]);
    }
    assert(mcf.flow(source, sink, n));
    long long ans{-mcf.minCost()};
    for (int i{} ; i < n ; i++) {
        ans += a[i] + b[i];
    }
    std::cout << ans << '\n';
}
accuracy: 1
time_ms: 200
memory_kb: 3976
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define repd(i,a,b) for (ll i=(a);i<(b);i++)
#define rep(i,n) repd(i,0,n)
#define all(x) (x).begin(),(x).end()
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return true; } return false; }
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return true; } return false; }
typedef long long ll;
typedef pair<ll,ll> P;
typedef vector<ll> vec;
using Graph = vector<vector<ll>>;
const long long INF = 1LL<<60;
const long long MOD = 1000000007;

//https://github.com/atcoder/ac-library
#ifndef ATCODER_INTERNAL_CSR_HPP
#define ATCODER_INTERNAL_CSR_HPP 1

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_CSR_HPP

#ifndef ATCODER_INTERNAL_QUEUE_HPP
#define ATCODER_INTERNAL_QUEUE_HPP 1

#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_QUEUE_HPP

#ifndef ATCODER_MINCOSTFLOW_HPP
#define ATCODER_MINCOSTFLOW_HPP 1

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    explicit mcf_graph(int n) : _n(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(_edges.size());
        _edges.push_back({from, to, cap, 0, cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(_edges.size());
        assert(0 <= i && i < m);
        return _edges[i];
    }
    std::vector<edge> edges() { return _edges; }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        int m = int(_edges.size());
        std::vector<int> edge_idx(m);

        auto g = [&]() {
            std::vector<int> degree(_n), redge_idx(m);
            std::vector<std::pair<int, _edge>> elist;
            elist.reserve(2 * m);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] = degree[e.from]++;
                redge_idx[i] = degree[e.to]++;
                elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}});
                elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}});
            }
            auto _g = internal::csr<_edge>(_n, elist);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] += _g.start[e.from];
                redge_idx[i] += _g.start[e.to];
                _g.elist[edge_idx[i]].rev = redge_idx[i];
                _g.elist[redge_idx[i]].rev = edge_idx[i];
            }
            return _g;
        }();

        auto result = slope(g, s, t, flow_limit);

        for (int i = 0; i < m; i++) {
            auto e = g.elist[edge_idx[i]];
            _edges[i].flow = _edges[i].cap - e.cap;
        }

        return result;
    }

  private:
    int _n;
    std::vector<edge> _edges;

    // inside edge
    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
                                            int s,
                                            int t,
                                            Cap flow_limit) {
        // variants (C = maxcost):
        // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
        // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge

        // dual_dist[i] = (dual[i], dist[i])
        std::vector<std::pair<Cost, Cost>> dual_dist(_n);
        std::vector<int> prev_e(_n);
        std::vector<bool> vis(_n);
        struct Q {
            Cost key;
            int to;
            bool operator<(Q r) const { return key > r.key; }
        };
        std::vector<int> que_min;
        std::vector<Q> que;
        auto dual_ref = [&]() {
            for (int i = 0; i < _n; i++) {
                dual_dist[i].second = std::numeric_limits<Cost>::max();
            }
            std::fill(vis.begin(), vis.end(), false);
            que_min.clear();
            que.clear();

            // que[0..heap_r) was heapified
            size_t heap_r = 0;

            dual_dist[s].second = 0;
            que_min.push_back(s);
            while (!que_min.empty() || !que.empty()) {
                int v;
                if (!que_min.empty()) {
                    v = que_min.back();
                    que_min.pop_back();
                } else {
                    while (heap_r < que.size()) {
                        heap_r++;
                        std::push_heap(que.begin(), que.begin() + heap_r);
                    }
                    v = que.front().to;
                    std::pop_heap(que.begin(), que.end());
                    que.pop_back();
                    heap_r--;
                }
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                // dist[v] = shortest(s, v) + dual[s] - dual[v]
                // dist[v] >= 0 (all reduced cost are positive)
                // dist[v] <= (n-1)C
                Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                    auto e = g.elist[i];
                    if (!e.cap) continue;
                    // |-dual[e.to] + dual[v]| <= (n-1)C
                    // cost <= C - -(n-1)C + 0 = nC
                    Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                    if (dual_dist[e.to].second - dist_v > cost) {
                        Cost dist_to = dist_v + cost;
                        dual_dist[e.to].second = dist_to;
                        prev_e[e.to] = e.rev;
                        if (dist_to == dist_v) {
                            que_min.push_back(e.to);
                        } else {
                            que.push_back(Q{dist_to, e.to});
                        }
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                // dual[v] = dual[v] - dist[t] + dist[v]
                //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) +
                //         (shortest(s, v) + dual[s] - dual[v]) = - shortest(s,
                //         t) + dual[t] + shortest(s, v) = shortest(s, v) -
                //         shortest(s, t) >= 0 - (n-1)C
                dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}};
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
            }
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                auto& e = g.elist[prev_e[v]];
                e.cap += c;
                g.elist[e.rev].cap -= c;
            }
            Cost d = -dual_dist[s].first;
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }
};

}  // namespace atcoder

#endif  // ATCODER_MINCOSTFLOW_HPP

using namespace atcoder;

template <typename T>
vector<T> compress(vector<T> &X) {
    // ソートした結果を vals に
    vector<T> vals = X;
    sort(vals.begin(), vals.end());
    // 隣り合う重複を削除(unique), 末端のゴミを削除(erase)
    vals.erase(unique(vals.begin(), vals.end()), vals.end());
    // 各要素ごとに二分探索で位置を求める
    for (int i = 0; i < (int)X.size(); i++) {
        X[i] = lower_bound(vals.begin(), vals.end(), X[i]) - vals.begin();
    }
    return vals;
}

int main()
{  
    ios::sync_with_stdio(false);
    cin.tie(0);
    ll n;cin>>n;
    vec a(n),b(n);
    rep(i,n)cin>>a[i]>>b[i];
    vec memo;
    rep(i,n){
        memo.push_back(a[i]);
        memo.push_back(b[i]);
    }
    compress(memo);
    mcf_graph<ll,ll> g(n*3+2);
    ll x=1e12;
    rep(i,n){
        g.add_edge(2+i,2+n+memo[2*i],1,x-b[i]);
        g.add_edge(2+i,2+n+memo[2*i+1],1,x-a[i]);
        g.add_edge(0,2+i,1,0);
        g.add_edge(2+n+2*i,1,1,0);
        g.add_edge(2+n+2*i+1,1,1,0);
    }
    g.add_edge(0,1,n,x);
    ll ans=g.flow(0,1,n).second;
    ans%=x;
    ans=x-ans;
    cout<<ans<<endl;
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 4084
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;
using pint = pair<int, int>;
using pll = pair<long long, long long>;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }

#define REP(i, n) for (long long i = 0; i < (long long)(n); ++i)
#define REP2(i, a, b) for (long long i = a; i < (long long)(b); ++i)
#define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, deque<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for(auto it : P) { s << "<" << it << "> "; } return s; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for(auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; }


// edge class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct Edge {
    int rev, from, to, id;
    FLOWTYPE cap, icap;
    COSTTYPE cost;
    Edge(int r, int f, int t, FLOWTYPE ca, COSTTYPE co, int id = -1) :
        rev(r), from(f), to(t), cap(ca), icap(ca), cost(co), id(id) {}
    friend ostream& operator << (ostream& s, const Edge& E) {
        if (E.cap > 0)
            return s << E.from << "->" << E.to <<
                '(' << E.cap << ',' << E.cost << ')';
        else return s;
    }
};

// graph class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct Graph {
    vector<vector<Edge<FLOWTYPE, COSTTYPE> > > list;
    
    Graph(int n = 0) : list(n) { }
    void init(int n = 0) { list.clear(); list.resize(n); }
    void reset() { for (int i = 0; i < (int)list.size(); ++i) for (int j = 0; j < list[i].size(); ++j) list[i][j].cap = list[i][j].icap; }
    inline vector<Edge<FLOWTYPE, COSTTYPE> >& operator [] (int i) { return list[i]; }
    inline const size_t size() const { return list.size(); }
    
    inline Edge<FLOWTYPE, COSTTYPE> &redge(const Edge<FLOWTYPE, COSTTYPE> &e) {
        if (e.from != e.to) return list[e.to][e.rev];
        else return list[e.to][e.rev + 1];
    }
    
    void addedge(int from, int to, FLOWTYPE cap, COSTTYPE cost, int id = -1) {
        list[from].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[to].size(), from, to, cap, cost, id));
        list[to].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[from].size() - 1, to, from, 0, -cost));
    }
    
    void add_undirected_edge(int from, int to, FLOWTYPE cap, COSTTYPE cost, int id = -1) {
        list[from].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[to].size(), from, to, cap, cost, id));
        list[to].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[from].size() - 1, to, from, cap, cost, id));
    }

    friend ostream& operator << (ostream& s, const Graph& G) {
        s << endl;
        for (int i = 0; i < G.size(); ++i) {
            s << i << ":";
            for (auto e : G.list[i]) s << " " << e;
            s << endl;
        }
        return s;
    }
};

// min-cost flow (by primal-dual)
template<class FLOWTYPE, class COSTTYPE> COSTTYPE MinCostFlow(Graph<FLOWTYPE, COSTTYPE> &G, int s, int t, FLOWTYPE f) {
    int n = (int)G.size();
    vector<COSTTYPE> dist(n, -1);
    vector<int> prevv(n), preve(n), seen(n, 0);
    COSTTYPE res = 0;
    while (f > 0) {
        seen.assign(n, 0);
        dist[s] = 0;
        seen[s] = true;
        while (true) {
            bool update = false;
            for (int v = 0; v < n; ++v) {
                if (!seen[v]) continue;
                for (int i = 0; i < G[v].size(); ++i) {
                    Edge<FLOWTYPE, COSTTYPE> &e = G[v][i];
                    if (e.cap > 0 && (!seen[e.to] || dist[e.to] > dist[v] + e.cost)) {
                        dist[e.to] = dist[v] + e.cost;
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        seen[e.to] = true;
                        update = true;
                    }
                }
            }
            if (!update) break;
        }
        if (!seen[t]) return -1;
        FLOWTYPE d = f;
        for (int v = t; v != s; v = prevv[v]) {
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += dist[t] * d;
        for (int v = t; v != s; v = prevv[v]) {
            Edge<FLOWTYPE, COSTTYPE> &e = G[prevv[v]][preve[v]];
            Edge<FLOWTYPE, COSTTYPE> &re = G.redge(e);
            e.cap -= d;
            re.cap += d;
        }
    }
    return res;
}

int main() {
    int N;
    cin >> N;
    vector<int> A(N), B(N), lens;
    for (int i = 0; i < N; ++i) {
        cin >> A[i] >> B[i];
        lens.push_back(A[i]);
        lens.push_back(B[i]);
    }
    sort(lens.begin(), lens.end());
    lens.erase(unique(lens.begin(), lens.end()), lens.end());
    
    // グラフ
    int source = N + lens.size(), sink = N + lens.size() + 1;
    Graph<int,int> G(N + lens.size() + 2);
    
    // ソース → 直方体
    for (int i = 0; i < N; ++i) {
        G.addedge(source, i, 1, 0);
    }
    // 長さ → シンク
    for (int i = 0; i < lens.size(); ++i) {
        G.addedge(i+N, sink, 1, 0);
    }
    // ソース → シンク
    G.addedge(source, sink, N, 0);
    // 直方体 → シンク
    for (int i = 0; i < N; ++i) {
        int aid = lower_bound(lens.begin(), lens.end(), A[i]) - lens.begin();
        int bid = lower_bound(lens.begin(), lens.end(), B[i]) - lens.begin();
        G.addedge(i, aid+N, 1, -B[i]);
        G.addedge(i, bid+N, 1, -A[i]);
    }
 
    int res = -MinCostFlow(G, source, sink, N);
    cout << res << endl;
}
accuracy: 1
time_ms: 180
memory_kb: 3928
final_rank: 9

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

struct Edge {
	long long to, cap, cost, rev;
};

class MincostFlow {
	public:
	int size_ = 0;
	vector<vector<Edge>> G;
	vector<long long> Potential;
	vector<long long> Dist;
	vector<long long> Prev_Verts;
	vector<long long> Prev_ID;
	vector<bool> Used;
	
	void Initialize(int sz) {
		size_ = sz + 2;
		G.resize(size_, vector<Edge>{});
		Potential.resize(size_, 0);
		Dist.resize(size_, 0);
		Prev_Verts.resize(size_, 0);
		Prev_ID.resize(size_, 0);
		Used.resize(size_, false);
	}
	
	void add_edge(long long u, long long v, long long f, long long c) {
		//cout << "(u, v) = (" << u << ", " << v << "), cap = " << f << ", cost = " << c << endl;
		G[u].push_back(Edge{ v,   f,  c, (int)G[v].size() });
		G[v].push_back(Edge{ u, 0LL, -c, (int)G[u].size() - 1 });
	}
	
	long long mincost_flow(int s, int t, int f, int Mode) {
		long long current_ans = 0;
		if (Mode == 1) {
			for (int i = 0; i < size_; i++) Potential[i] = 0;
		}
		
		// Loop Phase
		while (f > 0) {
			priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> Q;
			for (int i = 0; i < size_; i++) Dist[i] = (1LL << 60);
			for (int i = 0; i < size_; i++) Prev_Verts[i] = -1;
			for (int i = 0; i < size_; i++) Prev_ID[i] = -1;
			for (int i = 0; i < size_; i++) Used[i] = false;
			Dist[s] = 0;
			Q.push(make_pair(0, s));
			
			// Dijkstra
			while (!Q.empty()) {
				long long pos = Q.top().second; Q.pop();
				if (Used[pos] == true) continue;
				Used[pos] = true;
				for (int i = 0; i < (int)G[pos].size(); i++) {
					if (G[pos][i].cap == 0) continue;
					long long nex = G[pos][i].to;
					long long dst = G[pos][i].cost + Potential[pos] - Potential[nex];
					if (Dist[nex] > Dist[pos] + dst) {
						Dist[nex] = Dist[pos] + dst;
						Prev_Verts[nex] = pos;
						Prev_ID[nex] = i;
						Q.push(make_pair(Dist[nex], nex));
					}
				}
			}
			if (Dist[t] == (1LL << 60)) break;
			
			// Refresh Edges [Part 1]
			long long current_pos = t;
			long long current_min = f;
			while (current_pos != s) {
				int nex = Prev_Verts[current_pos];
				int idx = Prev_ID[current_pos];
				current_min = min(current_min, G[nex][idx].cap);
				current_pos = nex;
			}
			current_ans += 1LL * current_min * (Dist[t] + Potential[t] - Potential[s]);
			f -= current_min;
			
			// Refresh Edges [Part 2]
			current_pos = t;
			while (current_pos != s) {
				int nex = Prev_Verts[current_pos];
				int idx = Prev_ID[current_pos];
				G[nex][idx].cap -= current_min;
				G[G[nex][idx].to][G[nex][idx].rev].cap += current_min;
				current_pos = nex;
			}
			
			// Refresh Potential
			for (int i = 0; i < size_; i++) {
				if (Dist[i] != (1LL << 60)) Potential[i] += Dist[i];
			}
		}
		
		// Return
		return current_ans;
	}
};

long long N;
long long A[1009], B[1009];
long long X[2009], Y[2009];
vector<long long> Height[1000009];
MincostFlow Z;

int main() {
	// Input
	cin >> N;
	for (int i = 1; i <= N; i++) {
		cin >> A[i] >> B[i];
		X[i + 0] = A[i]; Y[i + 0] = B[i];
		X[i + N] = B[i]; Y[i + N] = A[i];
	}
	for (int i = 1; i <= 2 * N; i++) Height[X[i]].push_back(i);
	
	// Build a Graph [Part 1]
	Z.Initialize(6 * N + 10);
	int Count = 0;
	for (int i = 1; i <= 1000000; i++) {
		if (Height[i].size() == 0) continue;
		Count += 1;
		for (int j : Height[i]) {
			Z.add_edge(2 * N + Count, j, 1, 0);
		} 
	}
	for (int i = 1; i <= N; i++) {
		Z.add_edge(i + 0, 2 * N + Count + i, 1, 1200000000LL - Y[i + 0]);
		Z.add_edge(i + N, 2 * N + Count + i, 1, 1200000000LL - Y[i + N]);
	}
	
	// Build a Graph [Part 2]
	for (int i = 1; i <= Count; i++) Z.add_edge(3 * N + Count + 1, 2 * N + i        , 1, 0);
	for (int i = 1; i <=     N; i++) Z.add_edge(2 * N + Count + i, 3 * N + Count + 2, 1, 0);
	
	// Get Answer
	long long FinalAns = 0, current_cost = 0;
	for (int i = 1; i <= N; i++) {
		long long val = Z.mincost_flow(3 * N + Count + 1, 3 * N + Count + 2, 1, 2);
		if (val == 0) break;
		current_cost += val;
		FinalAns = max(FinalAns, 1200000000LL * i - current_cost);
	}
	
	// Output
	cout << FinalAns << endl;
	return 0;
}
accuracy: 1
time_ms: 390
memory_kb: 27780
final_rank: 17

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

struct Edge {
	long long to, cap, cost, rev;
};

class MincostFlow {
	public:
	int size_ = 0;
	vector<vector<Edge>> G;
	vector<long long> Potential;
	vector<long long> Dist;
	vector<long long> Prev_Verts;
	vector<long long> Prev_ID;
	vector<bool> Used;
	
	void Initialize(int sz) {
		size_ = sz + 2;
		G.resize(size_, vector<Edge>{});
		Potential.resize(size_, 0);
		Dist.resize(size_, 0);
		Prev_Verts.resize(size_, 0);
		Prev_ID.resize(size_, 0);
		Used.resize(size_, false);
	}
	
	void add_edge(long long u, long long v, long long f, long long c) {
		//cout << "(u, v) = (" << u << ", " << v << "), cap = " << f << ", cost = " << c << endl;
		G[u].push_back(Edge{ v,   f,  c, (int)G[v].size() });
		G[v].push_back(Edge{ u, 0LL, -c, (int)G[u].size() - 1 });
	}
	
	long long mincost_flow(int s, int t, int f, int Mode) {
		long long current_ans = 0;
		if (Mode == 1) {
			for (int i = 0; i < size_; i++) Potential[i] = 0;
		}
		
		// Loop Phase
		while (f > 0) {
			priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> Q;
			for (int i = 0; i < size_; i++) Dist[i] = (1LL << 60);
			for (int i = 0; i < size_; i++) Prev_Verts[i] = -1;
			for (int i = 0; i < size_; i++) Prev_ID[i] = -1;
			for (int i = 0; i < size_; i++) Used[i] = false;
			Dist[s] = 0;
			Q.push(make_pair(0, s));
			
			// Dijkstra
			while (!Q.empty()) {
				long long pos = Q.top().second; Q.pop();
				if (Used[pos] == true) continue;
				Used[pos] = true;
				for (int i = 0; i < (int)G[pos].size(); i++) {
					if (G[pos][i].cap == 0) continue;
					long long nex = G[pos][i].to;
					long long dst = G[pos][i].cost + Potential[pos] - Potential[nex];
					if (Dist[nex] > Dist[pos] + dst) {
						Dist[nex] = Dist[pos] + dst;
						Prev_Verts[nex] = pos;
						Prev_ID[nex] = i;
						Q.push(make_pair(Dist[nex], nex));
					}
				}
			}
			
			// Refresh Edges [Part 1]
			long long current_pos = t;
			long long current_min = f;
			while (current_pos != s) {
				int nex = Prev_Verts[current_pos];
				int idx = Prev_ID[current_pos];
				current_min = min(current_min, G[nex][idx].cap);
				current_pos = nex;
			}
			current_ans += 1LL * current_min * (Dist[t] + Potential[t] - Potential[s]);
			f -= current_min;
			
			// Refresh Edges [Part 2]
			current_pos = t;
			while (current_pos != s) {
				int nex = Prev_Verts[current_pos];
				int idx = Prev_ID[current_pos];
				G[nex][idx].cap -= current_min;
				G[G[nex][idx].to][G[nex][idx].rev].cap += current_min;
				current_pos = nex;
			}
			
			// Refresh Potential
			if (Dist[t] == (1LL << 60)) break;
			for (int i = 0; i < size_; i++) {
				if (Dist[i] != (1LL << 60)) Potential[i] += Dist[i];
			}
		}
		
		// Return
		return current_ans;
	}
};

long long N;
long long A[1009], B[1009];
long long X[2009], Y[2009];
vector<long long> Height[1000009];
MincostFlow Z;

int main() {
	// Input
	cin >> N;
	for (int i = 1; i <= N; i++) {
		cin >> A[i] >> B[i];
		X[i + 0] = A[i]; Y[i + 0] = B[i];
		X[i + N] = B[i]; Y[i + N] = A[i];
	}
	for (int i = 1; i <= 2 * N; i++) Height[X[i]].push_back(i);
	
	// Build a Graph [Part 1]
	Z.Initialize(6 * N + 10);
	int Count = 0;
	for (int i = 1; i <= 1000000; i++) {
		if (Height[i].size() == 0) continue;
		Count += 1;
		for (int j : Height[i]) {
			Z.add_edge(2 * N + Count, j, 1, 0);
		} 
	}
	for (int i = 1; i <= N; i++) {
		Z.add_edge(i + 0, 2 * N + Count + i, 1, 1200000000LL - Y[i + 0]);
		Z.add_edge(i + N, 2 * N + Count + i, 1, 1200000000LL - Y[i + N]);
	}
	
	// Build a Graph [Part 2]
	for (int i = 1; i <= Count; i++) Z.add_edge(3 * N + Count + 1, 2 * N + i        , 1, 0);
	for (int i = 1; i <=     N; i++) Z.add_edge(2 * N + Count + i, 3 * N + Count + 2, 1, 0);
	
	// Get Answer
	long long FinalAns = 0, current_cost = 0;
	for (int i = 1; i <= N; i++) {
		long long val = Z.mincost_flow(3 * N + Count + 1, 3 * N + Count + 2, 1, 2);
		if (val == 0) break;
		current_cost += val;
		FinalAns = max(FinalAns, 1200000000LL * i - current_cost);
	}
	
	// Output
	cout << FinalAns << endl;
	return 0;
}
accuracy: 0.16666666666666666
time_ms: 360
memory_kb: 27688
final_rank: 20

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

template <class F, class C> struct primal_dual {
    struct edge {
        int to;
        F cap;
        C cost;
        int rev;
        bool is_rev;
    };

    int n;
    vector<vector<edge>> g;
    vector<C> pot, d;
    vector<int> pv, pe;
    const C inf = numeric_limits<C>::max();

    primal_dual(int nn) : n(nn), g(nn) {}

    void add_edge(int from, int to, F cap, C cost) {
        g[from].push_back({to, cap, cost, (int)g[to].size(), false});
        g[to].push_back({from, 0, -cost, (int)g[from].size() - 1, true});
    }

    vector<pair<F, C>> min_cost_flow(int s, int t, F f_lim = numeric_limits<F>::max()) {
        F f = 0;
        C c = 0, cf = -1;
        vector<pair<F, C>> res = {{0, 0}};
        priority_queue<pair<C, int>, vector<pair<C, int>>, greater<pair<C, int>>> pq;
        pot.assign(n, 0);
        pe.assign(n, -1);
        pv.assign(n, -1);
        while (f < f_lim) {
            d.assign(n, inf);
            pq.emplace(0, s);
            d[s] = 0;
            while (!pq.empty()) {
                auto p = pq.top();
                C dv = p.first;
                int v = p.second;
                pq.pop();
                if (d[v] != dv) continue;
                for (int i = 0; i < (int)g[v].size(); i++) {
                    auto& e = g[v][i];
                    C nd = d[v] + e.cost + pot[v] - pot[e.to];
                    if (e.cap > 0 and d[e.to] > nd) {
                        d[e.to] = nd;
                        pv[e.to] = v;
                        pe[e.to] = i;
                        pq.emplace(d[e.to], e.to);
                    }
                }
            }
            if (d[t] == inf) break;
            for (int i = 0; i < n; i++) pot[i] += d[i];
            F add = f_lim - f;
            for (int v = t; v != s; v = pv[v]) {
                add = min(add, g[pv[v]][pe[v]].cap);
            }
            for (int v = t; v != s; v = pv[v]) {
                auto& e = g[pv[v]][pe[v]];
                e.cap -= add;
                g[v][e.rev].cap += add;
            }
            f += add;
            c += add * pot[t];
            if (cf == pot[t]) {
                res.pop_back();
            }
            cf = pot[t];
            res.emplace_back(f, c);
        }
        return res;
    }
};

constexpr int MAX = 1000010;

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int N;
    cin >> N;
    vector<int> A(N), B(N), C;
    for (int i = 0; i < N; i++) {
        cin >> A[i] >> B[i];
        C.emplace_back(A[i]);
        C.emplace_back(B[i]);
    }

    sort(C.begin(), C.end());
    C.erase(unique(C.begin(), C.end()), C.end());
    vector<int> idx(MAX, -1);
    int M = C.size();
    for (int i = 0; i < M; i++) idx[C[i]] = i;
    int s = N + M, t = s + 1;
    primal_dual<int, int> mcf(t + 1);
    for (int i = 0; i < N; i++) {
        mcf.add_edge(s, i, 1, 0);
        mcf.add_edge(i, N + idx[A[i]], 1, MAX - B[i]);
        mcf.add_edge(i, N + idx[B[i]], 1, MAX - A[i]);
        mcf.add_edge(i, t, 1, MAX);
    }
    for (int i = 0; i < M; i++) mcf.add_edge(N + i, t, 1, 0);
    int ans = MAX * N - mcf.min_cost_flow(s, t).back().second;
    cout << ans << '\n';
}
accuracy: 1
time_ms: 190
memory_kb: 7584
final_rank: 16

code_snippet: #pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    mcf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        g[from].push_back(_edge{to, int(g[to].size()), cap, cost});
        g[to].push_back(_edge{from, int(g[from].size()) - 1, 0, -cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{
            pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost,
        };
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result(m);
        for (int i = 0; i < m; i++) {
            result[i] = get_edge(i);
        }
        return result;
    }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);
        // variants (C = maxcost):
        // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
        // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge
        std::vector<Cost> dual(_n, 0), dist(_n);
        std::vector<int> pv(_n), pe(_n);
        std::vector<bool> vis(_n);
        auto dual_ref = [&]() {
            std::fill(dist.begin(), dist.end(),
                      std::numeric_limits<Cost>::max());
            std::fill(pv.begin(), pv.end(), -1);
            std::fill(pe.begin(), pe.end(), -1);
            std::fill(vis.begin(), vis.end(), false);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::priority_queue<Q> que;
            dist[s] = 0;
            que.push(Q{0, s});
            while (!que.empty()) {
                int v = que.top().to;
                que.pop();
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                // dist[v] = shortest(s, v) + dual[s] - dual[v]
                // dist[v] >= 0 (all reduced cost are positive)
                // dist[v] <= (n-1)C
                for (int i = 0; i < int(g[v].size()); i++) {
                    auto e = g[v][i];
                    if (vis[e.to] || !e.cap) continue;
                    // |-dual[e.to] + dual[v]| <= (n-1)C
                    // cost <= C - -(n-1)C + 0 = nC
                    Cost cost = e.cost - dual[e.to] + dual[v];
                    if (dist[e.to] - dist[v] > cost) {
                        dist[e.to] = dist[v] + cost;
                        pv[e.to] = v;
                        pe[e.to] = i;
                        que.push(Q{dist[e.to], e.to});
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                // dual[v] = dual[v] - dist[t] + dist[v]
                //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) + (shortest(s, v) + dual[s] - dual[v])
                //         = - shortest(s, t) + dual[t] + shortest(s, v)
                //         = shortest(s, v) - shortest(s, t) >= 0 - (n-1)C
                dual[v] -= dist[t] - dist[v];
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost = -1;
        std::vector<std::pair<Cap, Cost>> result;
        result.push_back({flow, cost});
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = pv[v]) {
                c = std::min(c, g[pv[v]][pe[v]].cap);
            }
            for (int v = t; v != s; v = pv[v]) {
                auto& e = g[pv[v]][pe[v]];
                e.cap -= c;
                g[v][e.rev].cap += c;
            }
            Cost d = -dual[s];
            flow += c;
            cost += c * d;
            if (prev_cost == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost = cost;
        }
        return result;
    }

  private:
    int _n;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

using namespace std;
using namespace atcoder;
const long long inf = 1LL << 40;



void solve(){
	int n;
	cin >> n;
	vector<long long> A(n), B(n);
	set<long long> se;
	for(int i = 0; i < n; i++){
		cin >> A[i] >> B[i];
		se.insert(A[i]);
		se.insert(B[i]);
	}
	map<long long, int> mp;
	int ind = 0;
	for(auto s:se){
		mp[s] = ind++;
	}

	int m = ind;
	mcf_graph<int, long long> G(n + m + 2);
	int s = n + m;
	int t = s + 1;
	for(int i = 0; i < n; i++){
		G.add_edge(s, i, 1, 0);
		G.add_edge(i, t, 1, inf);
	}
	for(int j = 0; j < m; j++) G.add_edge(n + j, t, 1, 0);
	for(int i = 0; i < n; i++){
		G.add_edge(i, n + mp[A[i]], 1, inf - B[i]);
		G.add_edge(i, n + mp[B[i]], 1, inf - A[i]);
	}
	auto tmp = G.flow(s, t);
	long long ans = inf * tmp.first - tmp.second;
	cout << ans << "\n";
}

int main(){
	cin.tie(0)->sync_with_stdio(0);
	int t;
	t = 1;
	// cin >> t;
	while(t--) solve();
}
accuracy: 1
time_ms: 40
memory_kb: 4216
final_rank: 5

code_snippet: #ifndef HIDDEN_IN_VS // 折りたたみ用

// 警告の抑制
#define _CRT_SECURE_NO_WARNINGS

// ライブラリの読み込み
#include <bits/stdc++.h>
using namespace std;

// 型名の短縮
using ll = long long; // -2^63 ～ 2^63 = 9 * 10^18（int は -2^31 ～ 2^31 = 2 * 10^9）
using pii = pair<int, int>;	using pll = pair<ll, ll>;	using pil = pair<int, ll>;	using pli = pair<ll, int>;
using vi = vector<int>;		using vvi = vector<vi>;		using vvvi = vector<vvi>;
using vl = vector<ll>;		using vvl = vector<vl>;		using vvvl = vector<vvl>;
using vb = vector<bool>;	using vvb = vector<vb>;		using vvvb = vector<vvb>;
using vc = vector<char>;	using vvc = vector<vc>;		using vvvc = vector<vvc>;
using vd = vector<double>;	using vvd = vector<vd>;		using vvvd = vector<vvd>;
template <class T> using priority_queue_rev = priority_queue<T, vector<T>, greater<T>>;
using Graph = vvi;

// 定数の定義
const double PI = acos(-1);
const vi DX = { 1, 0, -1, 0 }; // 4 近傍（下，右，上，左）
const vi DY = { 0, 1, 0, -1 };
int INF = 1001001001; ll INFL = 4004004004004004004LL;
double EPS = 1e-12;

// 入出力高速化
struct fast_io { fast_io() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(18); } } fastIOtmp;

// 汎用マクロの定義
#define all(a) (a).begin(), (a).end()
#define sz(x) ((int)(x).size())
#define lbpos(a, x) (int)distance((a).begin(), std::lower_bound(all(a), x))
#define ubpos(a, x) (int)distance((a).begin(), std::upper_bound(all(a), x))
#define Yes(b) {cout << ((b) ? "Yes\n" : "No\n");}
#define rep(i, n) for(int i = 0, i##_len = int(n); i < i##_len; ++i) // 0 から n-1 まで昇順
#define repi(i, s, t) for(int i = int(s), i##_end = int(t); i <= i##_end; ++i) // s から t まで昇順
#define repir(i, s, t) for(int i = int(s), i##_end = int(t); i >= i##_end; --i) // s から t まで降順
#define repe(v, a) for(const auto& v : (a)) // a の全要素（変更不可能）
#define repea(v, a) for(auto& v : (a)) // a の全要素（変更可能）
#define repb(set, d) for(int set = 0; set < (1 << int(d)); ++set) // d ビット全探索（昇順）
#define repp(a) sort(all(a)); for(bool a##_perm = true; a##_perm; a##_perm = next_permutation(all(a))) // a の順列全て（昇順）
#define smod(n, m) ((((n) % (m)) + (m)) % (m)) // 非負mod
#define uniq(a) {sort(all(a)); (a).erase(unique(all(a)), (a).end());} // 重複除去
#define EXIT(a) {cout << (a) << endl; exit(0);} // 強制終了

// 汎用関数の定義
template <class T> inline ll pow(T n, int k) { ll v = 1; rep(i, k) v *= n; return v; }
template <class T> inline bool chmax(T& M, const T& x) { if (M < x) { M = x; return true; } return false; } // 最大値を更新（更新されたら true を返す）
template <class T> inline bool chmin(T& m, const T& x) { if (m > x) { m = x; return true; } return false; } // 最小値を更新（更新されたら true を返す）

// 演算子オーバーロード
template <class T, class U> inline istream& operator>>(istream& is, pair<T, U>& p) { is >> p.first >> p.second; return is; }
template <class T> inline istream& operator>>(istream& is, vector<T>& v) { repea(x, v) is >> x; return is; }
template <class T> inline vector<T>& operator--(vector<T>& v) { repea(x, v) --x; return v; }
template <class T> inline vector<T>& operator++(vector<T>& v) { repea(x, v) ++x; return v; }

// 手元環境（Visual Studio）
#ifdef _MSC_VER
#include "local.hpp"
// 提出用（gcc）
#else
inline int popcount(int n) { return __builtin_popcount(n); }
inline int popcount(ll n) { return __builtin_popcountll(n); }
inline int lsb(int n) { return n != 0 ? __builtin_ctz(n) : -1; }
inline int lsb(ll n) { return n != 0 ? __builtin_ctzll(n) : -1; }
inline int msb(int n) { return n != 0 ? (31 - __builtin_clz(n)) : -1; }
inline int msb(ll n) { return n != 0 ? (63 - __builtin_clzll(n)) : -1; }
#define gcd __gcd
#define dump(...)
#define dumpel(v)
#define dump_list(v)
#define input_from_file(f)
#define output_to_file(f)
#define Assert(b) { if (!(b)) while (1) cout << "OLE"; }
#endif

#endif // 折りたたみ用


////--------------AtCoder 専用--------------
//#include <atcoder/all>
//using namespace atcoder;
//
////using mint = modint1000000007;
//using mint = modint998244353;
////using mint = modint; // mint::set_mod(m);
//
//istream& operator>>(istream& is, mint& x) { ll x_; is >> x_; x = x_; return is; }
//ostream& operator<<(ostream& os, const mint& x) { os << x.val(); return os; }
//using vm = vector<mint>; using vvm = vector<vm>; using vvvm = vector<vvm>;
////----------------------------------------


#ifndef ATCODER_INTERNAL_CSR_HPP
#define ATCODER_INTERNAL_CSR_HPP 1

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
    namespace internal {

        template <class E> struct csr {
            std::vector<int> start;
            std::vector<E> elist;
            explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
                : start(n + 1), elist(edges.size()) {
                for (auto e : edges) {
                    start[e.first + 1]++;
                }
                for (int i = 1; i <= n; i++) {
                    start[i] += start[i - 1];
                }
                auto counter = start;
                for (auto e : edges) {
                    elist[counter[e.first]++] = e.second;
                }
            }
        };

    }  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_CSR_HPP


#ifndef ATCODER_INTERNAL_QUEUE_HPP
#define ATCODER_INTERNAL_QUEUE_HPP 1

#include <vector>

namespace atcoder {

    namespace internal {

        template <class T> struct simple_queue {
            std::vector<T> payload;
            int pos = 0;
            void reserve(int n) { payload.reserve(n); }
            int size() const { return int(payload.size()) - pos; }
            bool empty() const { return pos == int(payload.size()); }
            void push(const T& t) { payload.push_back(t); }
            T& front() { return payload[pos]; }
            void clear() {
                payload.clear();
                pos = 0;
            }
            void pop() { pos++; }
        };

    }  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_QUEUE_HPP


#ifndef ATCODER_MINCOSTFLOW_HPP
#define ATCODER_MINCOSTFLOW_HPP 1

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

    template <class Cap, class Cost> struct mcf_graph {
    public:
        mcf_graph() {}
        explicit mcf_graph(int n) : _n(n) {}

        int add_edge(int from, int to, Cap cap, Cost cost) {
            assert(0 <= from && from < _n);
            assert(0 <= to && to < _n);
            assert(0 <= cap);
            assert(0 <= cost);
            int m = int(_edges.size());
            _edges.push_back({ from, to, cap, 0, cost });
            return m;
        }

        struct edge {
            int from, to;
            Cap cap, flow;
            Cost cost;
        };

        edge get_edge(int i) {
            int m = int(_edges.size());
            assert(0 <= i && i < m);
            return _edges[i];
        }
        std::vector<edge> edges() { return _edges; }

        std::pair<Cap, Cost> flow(int s, int t) {
            return flow(s, t, std::numeric_limits<Cap>::max());
        }
        std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
            return slope(s, t, flow_limit).back();
        }
        std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
            return slope(s, t, std::numeric_limits<Cap>::max());
        }
        std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
            assert(0 <= s && s < _n);
            assert(0 <= t && t < _n);
            assert(s != t);

            int m = int(_edges.size());
            std::vector<int> edge_idx(m);

            auto g = [&]() {
                std::vector<int> degree(_n), redge_idx(m);
                std::vector<std::pair<int, _edge>> elist;
                elist.reserve(2 * m);
                for (int i = 0; i < m; i++) {
                    auto e = _edges[i];
                    edge_idx[i] = degree[e.from]++;
                    redge_idx[i] = degree[e.to]++;
                    elist.push_back({ e.from, {e.to, -1, e.cap - e.flow, e.cost} });
                    elist.push_back({ e.to, {e.from, -1, e.flow, -e.cost} });
                }
                auto _g = internal::csr<_edge>(_n, elist);
                for (int i = 0; i < m; i++) {
                    auto e = _edges[i];
                    edge_idx[i] += _g.start[e.from];
                    redge_idx[i] += _g.start[e.to];
                    _g.elist[edge_idx[i]].rev = redge_idx[i];
                    _g.elist[redge_idx[i]].rev = edge_idx[i];
                }
                return _g;
            }();

            auto result = slope(g, s, t, flow_limit);

            for (int i = 0; i < m; i++) {
                auto e = g.elist[edge_idx[i]];
                _edges[i].flow = _edges[i].cap - e.cap;
            }

            return result;
        }

    private:
        int _n;
        std::vector<edge> _edges;

        // inside edge
        struct _edge {
            int to, rev;
            Cap cap;
            Cost cost;
        };

        std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
            int s,
            int t,
            Cap flow_limit) {
            // variants (C = maxcost):
            // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
            // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge

            // dual_dist[i] = (dual[i], dist[i])
            std::vector<std::pair<Cost, Cost>> dual_dist(_n);
            std::vector<int> prev_e(_n);
            std::vector<bool> vis(_n);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::vector<int> que_min;
            std::vector<Q> que;
            auto dual_ref = [&]() {
                for (int i = 0; i < _n; i++) {
                    dual_dist[i].second = std::numeric_limits<Cost>::max();
                }
                std::fill(vis.begin(), vis.end(), false);
                que_min.clear();
                que.clear();

                // que[0..heap_r) was heapified
                size_t heap_r = 0;

                dual_dist[s].second = 0;
                que_min.push_back(s);
                while (!que_min.empty() || !que.empty()) {
                    int v;
                    if (!que_min.empty()) {
                        v = que_min.back();
                        que_min.pop_back();
                    }
                    else {
                        while (heap_r < que.size()) {
                            heap_r++;
                            std::push_heap(que.begin(), que.begin() + heap_r);
                        }
                        v = que.front().to;
                        std::pop_heap(que.begin(), que.end());
                        que.pop_back();
                        heap_r--;
                    }
                    if (vis[v]) continue;
                    vis[v] = true;
                    if (v == t) break;
                    // dist[v] = shortest(s, v) + dual[s] - dual[v]
                    // dist[v] >= 0 (all reduced cost are positive)
                    // dist[v] <= (n-1)C
                    Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                    for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                        auto e = g.elist[i];
                        if (!e.cap) continue;
                        // |-dual[e.to] + dual[v]| <= (n-1)C
                        // cost <= C - -(n-1)C + 0 = nC
                        Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                        if (dual_dist[e.to].second - dist_v > cost) {
                            Cost dist_to = dist_v + cost;
                            dual_dist[e.to].second = dist_to;
                            prev_e[e.to] = e.rev;
                            if (dist_to == dist_v) {
                                que_min.push_back(e.to);
                            }
                            else {
                                que.push_back(Q{ dist_to, e.to });
                            }
                        }
                    }
                }
                if (!vis[t]) {
                    return false;
                }

                for (int v = 0; v < _n; v++) {
                    if (!vis[v]) continue;
                    // dual[v] = dual[v] - dist[t] + dist[v]
                    //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) +
                    //         (shortest(s, v) + dual[s] - dual[v]) = - shortest(s,
                    //         t) + dual[t] + shortest(s, v) = shortest(s, v) -
                    //         shortest(s, t) >= 0 - (n-1)C
                    dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
                }
                return true;
            };
            Cap flow = 0;
            Cost cost = 0, prev_cost_per_flow = -1;
            std::vector<std::pair<Cap, Cost>> result = { {Cap(0), Cost(0)} };
            while (flow < flow_limit) {
                if (!dual_ref()) break;
                Cap c = flow_limit - flow;
                for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                    c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
                }
                for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                    auto& e = g.elist[prev_e[v]];
                    e.cap += c;
                    g.elist[e.rev].cap -= c;
                }
                Cost d = -dual_dist[s].first;
                flow += c;
                cost += c * d;
                if (prev_cost_per_flow == d) {
                    result.pop_back();
                }
                result.push_back({ flow, cost });
                prev_cost_per_flow = d;
            }
            return result;
        }
    };

}  // namespace atcoder

#endif  // ATCODER_MINCOSTFLOW_HPP


using namespace atcoder;


//【最小費用流（負コスト可，負コスト閉路なし）】
/*
* Negative_mcf_graph(int n) : O(1)
*	n 頂点で初期化する．
* 
* add_edge(int s, int t, ll cap, ll cost) : O(1)
*	s から t へ容量 cap，コスト cost の辺を追加する．
* 
* pll flow(int ST, int GL, ll f_lim = INFL) : O(F (n + m) log n)（F:流量，m:辺の数）
*	ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
*	制約：負コストの閉路は存在しない
*/
struct Negative_mcf_graph {
	// 参考 : https://ikatakos.com/pot/programming_algorithm/graph_theory/minimum_cost_flow

	// n : 頂点数
	int n;

	// 辺
	struct Edge {
		int to;
		ll cap, cost;

		Edge(int to_, ll cap_, ll cost_) : to(to_), cap(cap_), cost(cost_) {}
	};

	// 元のグラフ（負辺あり）
	vector<vector<Edge>> g;

	// pot[s] : 頂点 s のポテンシャル
	vl pot;

	// n 頂点で初期化する．
	Negative_mcf_graph(int n_) : n(n_), g(n), pot(n, INFL) {}

	// s から t へ容量 cap，コスト cost の辺を追加する．
	void add_edge(int s, int t, ll cap, ll cost) {
		// verify : https://atcoder.jp/contests/code-festival-2014-china-open/tasks/code_festival_china_h
		
		g[s].emplace_back(t, cap, cost);
	}

	// ベルマンフォード法で ST からの距離を求め，それをポテンシャルとする．
	bool bellman_ford(int ST) {
		pot[ST] = 0;

		rep(i, n) {
			bool updated = false;

			// 全ての辺についての操作
			rep(s, n) repe(e, g[s]) {
				// もし (始点への距離) + (辺のコスト) < (終点への距離) なら (終点への距離) を更新する．
				// INFL からは何を引いても INFL になるようにしているので，ST から到達可能な負閉路しか検出しない．
				if (pot[s] != INFL && pot[s] + e.cost < pot[e.to]) {
					pot[e.to] = pot[s] + e.cost;
					updated = true;
				}
			}

			// もし距離の更新が起こらなければ最短距離確定
			if (!updated) return true;
		}

		// もし全ての辺についての操作を n 回繰り返しても距離の更新があったなら，
		// ST から到達可能な負の閉路を持っているので false を返す．
		return false;
	}

	// ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
	pll flow(int ST, int GL, ll f_lim = INFL) {
		// verify : https://atcoder.jp/contests/code-festival-2014-china-open/tasks/code_festival_china_h

		// ベルマンフォード法で ST から v までの距離を求め，それをポテンシャル pot[v] とする．
		bool non_neg_cyc = bellman_ford(ST);
		Assert(non_neg_cyc);

		// g_pos : 辺 s→t のコストが (元の辺のコスト) - (pot[t] - pot[s]) >= 0 であるようなグラフ
		mcf_graph<ll, ll> g_pos(n);
		rep(s, n) repe(e, g[s]) {
			g_pos.add_edge(s, e.to, e.cap, e.cost - (pot[e.to] - pot[s]));
		}

		// g_pos の最小費用流を求める．
		ll cap, cost;
		tie(cap, cost) = g_pos.flow(ST, GL, f_lim);

		// 実際のコストは (流量) * pot[GL] を加えたものになる．
		cost += cap * pot[GL];

		return make_pair(cap, cost);
	}
};


int main() {
//	input_from_file("input.txt");
//	output_to_file("output.txt");
	
	int n;
	cin >> n;

	vl a(n), b(n);
	rep(i, n) cin >> a[i] >> b[i];

	vector<tuple<ll, ll, int>> whi(2 * n);
	unordered_map<ll, int> w_to_id; int id = 0;

	rep(i, n) {
		whi[2 * i] = { a[i], b[i], i };
		if (!w_to_id.count(a[i])) w_to_id[a[i]] = id++;

		whi[2 * i + 1] = { b[i], a[i], i };
		if (!w_to_id.count(b[i])) w_to_id[b[i]] = id++;
	}

	sort(all(whi));

	int ST = n + (2 * n) + id, GL = ST + 1;
	Negative_mcf_graph g(GL + 1);

	rep(i, n) g.add_edge(ST, i, 1, 0);
	rep(t, 2 * n) {
		ll w, h; int i;
		tie(w, h, i) = whi[t];

		g.add_edge(i, n + t, 1, 0);
		g.add_edge(n + t, n + 2 * n + w_to_id[w], 1, -h);
	}
	rep(j, id) g.add_edge(n + 2 * n + j, GL, 1, 0);

	// グラフ内にフローを流さなくてもいいように側溝を用意する．
	g.add_edge(ST, GL, INFL, 0);

	ll cap, cost;
	tie(cap, cost) = g.flow(ST, GL, n);
	dump(cap, cost);

	cout << -cost << endl;
}
accuracy: 1
time_ms: 40
memory_kb: 4728
final_rank: 6

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

template< typename flow_t, typename cost_t >
struct PrimalDual {
    const cost_t INF;
    
    struct edge {
        int to;
        flow_t cap;
        cost_t cost;
        int rev;
        bool isrev;
    };
    vector< vector< edge > > graph;
    vector< cost_t > potential, min_cost;
    vector< int > prevv, preve;
    
    PrimalDual(int V) : graph(V), INF(numeric_limits< cost_t >::max()) {}
    
    void add_edge(int from, int to, flow_t cap, cost_t cost) {
        graph[from].emplace_back((edge) {to, cap, cost, (int) graph[to].size(), false});
        graph[to].emplace_back((edge) {from, 0, -cost, (int) graph[from].size() - 1, true});
    }
    
    cost_t min_cost_flow(int s, int t, flow_t f) {
        int V = (int) graph.size();
        cost_t ret = 0;
        using Pi = pair< cost_t, int >;
        priority_queue< Pi, vector< Pi >, greater< Pi > > que;
        potential.assign(V, 0);
        preve.assign(V, -1);
        prevv.assign(V, -1);
        
        while(f > 0) {
            min_cost.assign(V, INF);
            que.emplace(0, s);
            min_cost[s] = 0;
            while(!que.empty()) {
                Pi p = que.top();
                que.pop();
                if(min_cost[p.second] < p.first) continue;
                for(int i = 0; i < graph[p.second].size(); i++) {
                    edge &e = graph[p.second][i];
                    cost_t nextCost = min_cost[p.second] + e.cost + potential[p.second] - potential[e.to];
                    if(e.cap > 0 && min_cost[e.to] > nextCost) {
                        min_cost[e.to] = nextCost;
                        prevv[e.to] = p.second, preve[e.to] = i;
                        que.emplace(min_cost[e.to], e.to);
                    }
                }
            }
            if(min_cost[t] == INF) return -1;
            for(int v = 0; v < V; v++) potential[v] += min_cost[v];
            flow_t addflow = f;
            for(int v = t; v != s; v = prevv[v]) {
                addflow = min(addflow, graph[prevv[v]][preve[v]].cap);
            }
            f -= addflow;
            ret += addflow * potential[t];
            for(int v = t; v != s; v = prevv[v]) {
                edge &e = graph[prevv[v]][preve[v]];
                e.cap -= addflow;
                graph[v][e.rev].cap += addflow;
            }
        }
        return ret;
    }
};

int main() {
    int N;
    cin >> N;
    vector<int>A(N),B(N),C(1000001);
    PrimalDual<int,int>flow(N*4+1000000+2);
    int S = N*4+1000000,T = S+1;
    int ans = 0;
    for(int i = 0; i < N; i++) {
        cin >> A[i] >> B[i];
        C[A[i]] = C[B[i]] = 1;
        ans += max(A[i],B[i]);
        flow.add_edge(S,i,1,0);
        flow.add_edge(i,N+i,1,max(A[i],B[i]));
        flow.add_edge(i,2*N+i,1,max(A[i],B[i])-min(A[i],B[i]));
        flow.add_edge(i,3*N+i,1,0);
        flow.add_edge(N+i,T,1,0);
        flow.add_edge(2*N+i,4*N+max(A[i],B[i])-1,1,0);
        flow.add_edge(3*N+i,4*N+min(A[i],B[i])-1,1,0);
    }
    for(int i = 1; i <= 1000000; i++) {
        if(C[i]) {
            flow.add_edge(4*N+i-1,T,1,0);
        }
    }
    cout << ans-flow.min_cost_flow(S,T,N) << endl;
}
accuracy: 1
time_ms: 1270
memory_kb: 46764
final_rank: 18

code_snippet: #line 1 "a.cpp"
/**
 *	author: nok0
 *	created: 2022.04.06 21:05:07
**/
#line 1 "/Users/nok0/Documents/Programming/nok0/cftemp.hpp"
#include <bits/stdc++.h>
using namespace std;

#pragma region Macros
// rep macro
#define foa(v, a) for(auto &v : a)
#define REPname(a, b, c, d, e, ...) e
#define REP(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int i = 0; i < (x); ++i)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))
#define REPSname(a, b, c, ...) c
#define REPS(...) REPSname(__VA_ARGS__, REPS1, REPS0)(__VA_ARGS__)
#define REPS0(x) for(int i = 1; i <= (x); ++i)
#define REPS1(i, x) for(int i = 1; i <= (x); ++i)
#define RREPname(a, b, c, d, e, ...) e
#define RREP(...) RREPname(__VA_ARGS__, RREP3, RREP2, RREP1, RREP0)(__VA_ARGS__)
#define RREP0(x) for(int i = (x)-1; i >= 0; --i)
#define RREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define RREP2(i, r, l) for(int i = (r)-1; i >= (l); --i)
#define RREP3(i, r, l, c) for(int i = (r)-1; i >= (l); i -= (c))
#define RREPSname(a, b, c, ...) c
#define RREPS(...) RREPSname(__VA_ARGS__, RREPS1, RREPS0)(__VA_ARGS__)
#define RREPS0(x) for(int i = (x); i >= 1; --i)
#define RREPS1(i, x) for(int i = (x); i >= 1; --i)

// name macro
#define pb push_back
#define eb emplace_back
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define popcnt(x) __builtin_popcountll(x)
template <class T = int>
using V = std::vector<T>;
template <class T = int>
using VV = std::vector<std::vector<T>>;
template <class T>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
using ll = long long;
using ld = long double;
using int128 = __int128_t;
using pii = std::pair<int, int>;
using pll = std::pair<long long, long long>;

// input macro
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
	is >> p.first >> p.second;
	return is;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
	for(T &i : v) is >> i;
	return is;
}
std::istream &operator>>(std::istream &is, __int128_t &a) {
	std::string s;
	is >> s;
	__int128_t ret = 0;
	for(int i = 0; i < s.length(); i++)
		if('0' <= s[i] and s[i] <= '9')
			ret = 10 * ret + s[i] - '0';
	a = ret * (s[0] == '-' ? -1 : 1);
	return is;
}
namespace scanner {
void scan(int &a) { std::cin >> a; }
void scan(long long &a) { std::cin >> a; }
void scan(std::string &a) { std::cin >> a; }
void scan(char &a) { std::cin >> a; }
void scan(char a[]) { std::scanf("%s", a); }
void scan(double &a) { std::cin >> a; }
void scan(long double &a) { std::cin >> a; }
template <class T, class U>
void scan(std::pair<T, U> &p) { std::cin >> p; }
template <class T>
void scan(std::vector<T> &a) { std::cin >> a; }
void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &...tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner
#define VEC(type, name, size)     \
	std::vector<type> name(size); \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                                    \
	std::vector<std::vector<type>> name(h, std::vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)             \
	std::string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHAR(...)     \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DOUBLE(...)     \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)

// output-macro
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
	os << p.first << " " << p.second;
	return os;
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &a) {
	for(int i = 0; i < int(a.size()); ++i) {
		if(i) os << " ";
		os << a[i];
	}
	return os;
}
std::ostream &operator<<(std::ostream &dest, __int128_t &value) {
	std::ostream::sentry s(dest);
	if(s) {
		__uint128_t tmp = value < 0 ? -value : value;
		char buffer[128];
		char *d = std::end(buffer);
		do {
			--d;
			*d = "0123456789"[tmp % 10];
			tmp /= 10;
		} while(tmp != 0);
		if(value < 0) {
			--d;
			*d = '-';
		}
		int len = std::end(buffer) - d;
		if(dest.rdbuf()->sputn(d, len) != len) {
			dest.setstate(std::ios_base::badbit);
		}
	}
	return dest;
}
template <class T>
void print(const T a) { std::cout << a << '\n'; }
template <class Head, class... Tail>
void print(Head H, Tail... T) {
	std::cout << H << ' ';
	print(T...);
}
template <class T>
void printel(const T a) { std::cout << a << '\n'; }
template <class T>
void printel(const std::vector<T> &a) {
	for(const auto &v : a)
		std::cout << v << '\n';
}
template <class Head, class... Tail>
void printel(Head H, Tail... T) {
	std::cout << H << '\n';
	printel(T...);
}
void Yes(const bool b = true) { std::cout << (b ? "Yes\n" : "No\n"); }
void No() { std::cout << "No\n"; }
void YES(const bool b = true) { std::cout << (b ? "YES\n" : "NO\n"); }
void NO() { std::cout << "NO\n"; }
void err(const bool b = true) {
	if(b) {
		std::cout << "-1\n", exit(0);
	}
}

//debug macro
namespace debugger {
template <class T>
void view(const std::vector<T> &a) {
	std::cerr << "{ ";
	for(const auto &v : a) {
		std::cerr << v << ", ";
	}
	std::cerr << "\b\b }";
}
template <class T>
void view(const std::vector<std::vector<T>> &a) {
	std::cerr << "{\n";
	for(const auto &v : a) {
		std::cerr << "\t";
		view(v);
		std::cerr << "\n";
	}
	std::cerr << "}";
}
template <class T, class U>
void view(const std::vector<std::pair<T, U>> &a) {
	std::cerr << "{\n";
	for(const auto &p : a) std::cerr << "\t(" << p.first << ", " << p.second << ")\n";
	std::cerr << "}";
}
template <class T, class U>
void view(const std::map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &p : m) std::cerr << "\t[" << p.first << "] : " << p.second << "\n";
	std::cerr << "}";
}
template <class T, class U>
void view(const std::pair<T, U> &p) { std::cerr << "(" << p.first << ", " << p.second << ")"; }
template <class T>
void view(const std::set<T> &s) {
	std::cerr << "{ ";
	for(auto &v : s) {
		view(v);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <class T>
void view(const T &e) { std::cerr << e; }
}  // namespace debugger
#ifdef LOCAL
void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	debugger::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "\b\b]\n";                                   \
	} while(false)
#else
#define debug(...) (void(0))
#endif

// vector macro
template <class T>
int lb(const std::vector<T> &a, const T x) { return std::distance((a).begin(), std::lower_bound((a).begin(), (a).end(), (x))); }
template <class T>
int ub(const std::vector<T> &a, const T x) { return std::distance((a).begin(), std::upper_bound((a).begin(), (a).end(), (x))); }
template <class T>
void UNIQUE(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<T> press(std::vector<T> &a) {
	auto res = a;
	UNIQUE(res);
	for(auto &v : a)
		v = lb(res, v);
	return res;
}
#define SORTname(a, b, c, ...) c
#define SORT(...) SORTname(__VA_ARGS__, SORT1, SORT0, ...)(__VA_ARGS__)
#define SORT0(a) std::sort((a).begin(), (a).end())
#define SORT1(a, c) std::sort((a).begin(), (a).end(), [](const auto x, const auto y) { return x c y; })
template <class T>
void ADD(std::vector<T> &a, const T x = 1) {
	for(auto &v : a) v += x;
}
template <class T>
void SUB(std::vector<T> &a, const T x = 1) {
	for(auto &v : a) v -= x;
}
std::vector<std::pair<char, int>> rle(const string &s) {
	int n = s.size();
	std::vector<std::pair<char, int>> ret;
	for(int l = 0; l < n;) {
		int r = l + 1;
		for(; r < n and s[l] == s[r]; r++) {}
		ret.emplace_back(s[l], r - l);
		l = r;
	}
	return ret;
}
template <class T>
std::vector<std::pair<T, int>> rle(const std::vector<T> &v) {
	int n = v.size();
	std::vector<std::pair<T, int>> ret;
	for(int l = 0; l < n;) {
		int r = l + 1;
		for(; r < n and v[l] == v[r]; r++) {}
		ret.emplace_back(v[l], r - l);
		l = r;
	}
	return ret;
}
std::vector<int> iota(int n) {
	std::vector<int> p(n);
	std::iota(p.begin(), p.end(), 0);
	return p;
}
template <class T>
struct cum_vector {
public:
	cum_vector() = default;
	template <class U>
	cum_vector(const std::vector<U> &vec) : cum((int)vec.size() + 1) {
		for(int i = 0; i < (int)vec.size(); i++)
			cum[i + 1] = cum[i] + vec[i];
	}
	T prod(int l, int r) {
		return cum[r] - cum[l];
	}

private:
	std::vector<T> cum;
};

// math macro
template <class T, class U>
inline bool chmin(T &a, const U &b) { return a > b ? a = b, true : false; }
template <class T, class U>
inline bool chmax(T &a, const U &b) { return a < b ? a = b, true : false; }
template <class T>
T divup(T x, T y) { return (x + y - 1) / y; }
template <class T>
T POW(T a, long long n) {
	T ret = 1;
	while(n) {
		if(n & 1) ret *= a;
		a *= a;
		n >>= 1;
	}
	return ret;
}
// modpow
long long POW(long long a, long long n, const int mod) {
	long long ret = 1;
	a = (a % mod + mod) % mod;
	while(n) {
		if(n & 1) (ret *= a) %= mod;
		(a *= a) %= mod;
		n >>= 1;
	}
	return ret;
}
template <class T, class F>
T bin_search(T ok, T ng, const F &f) {
	while(abs(ok - ng) > 1) {
		T mid = (ok + ng) >> 1;
		(f(mid) ? ok : ng) = mid;
	}
	return ok;
}
template <class T, class F>
T bin_search(T ok, T ng, const F &f, int loop) {
	for(int i = 0; i < loop; i++) {
		T mid = (ok + ng) / 2;
		(f(mid) ? ok : ng) = mid;
	}
	return ok;
}

// others
struct fast_io {
	fast_io() {
		ios::sync_with_stdio(false);
		cin.tie(nullptr);
		cout << fixed << setprecision(15);
	}
} fast_io_;
const int inf = 1e9;
const ll INF = 1e18;
#pragma endregion

void main_();

int main() {
	main_();
	return 0;
}
#line 7 "/Users/nok0/Documents/Programming/nok0/graph/mincost_b_flow.hpp"

enum Objective {
	MINIMIZE = 1,
	MAXIMIZE = -1,
};
enum class Status {
	OPTIMAL,
	INFEASIBLE,
};

template <class Flow, class Cost, Objective objective = Objective::MINIMIZE, Flow SCALING_FACTOR = 2>
struct MinCostFlow {
private:
	using V_id = uint32_t;
	using E_id = uint32_t;

	struct Edge {
		friend struct MinCostFlow;

	private:
		V_id src, dst;
		Flow flow, cap;
		Cost cost;
		E_id rev;

	public:
		Edge() = default;

		Edge(const V_id src, const V_id dst, const Flow cap, const Cost cost,
		     const E_id rev)
		  : src(src), dst(dst), flow(0), cap(cap), cost(cost), rev(rev) {}

		[[nodiscard]] Flow residual_cap() const { return cap - flow; }
	};

public:
	struct EdgePtr {
		friend struct MinCostFlow;

	private:
		const MinCostFlow *instance;
		const V_id v;
		const E_id e;

		EdgePtr(const MinCostFlow *instance, const V_id v, const E_id e)
		  : instance(instance), v(v), e(e) {}

		[[nodiscard]] const Edge &edge() const { return instance->g[v][e]; }

		[[nodiscard]] const Edge &rev() const {
			const Edge &e = edge();
			return instance->g[e.dst][e.rev];
		}

	public:
		[[nodiscard]] V_id src() const { return rev().dst; }

		[[nodiscard]] V_id dst() const { return edge().dst; }

		[[nodiscard]] Flow flow() const { return edge().flow; }

		[[nodiscard]] Flow lower() const { return -rev().cap; }

		[[nodiscard]] Flow upper() const { return edge().cap; }

		[[nodiscard]] Cost cost() const { return edge().cost; }

		[[nodiscard]] Cost gain() const { return -edge().cost; }
	};

private:
	V_id n;
	std::vector<std::vector<Edge>> g;
	std::vector<Flow> b;

public:
	MinCostFlow() : n(0) {}

	V_id add_vertex() {
		++n;
		g.resize(n);
		b.resize(n);
		return n - 1;
	}

	std::vector<V_id> add_vertices(const size_t size) {
		std::vector<V_id> ret;
		for(V_id i = 0; i < size; ++i) ret.emplace_back(n + i);
		n += size;
		g.resize(n);
		b.resize(n);
		return ret;
	}

	EdgePtr add_edge(const V_id src, const V_id dst, const Flow lower, const Flow upper, const Cost cost) {
		const E_id e = g[src].size(), re = src == dst ? e + 1 : g[dst].size();
		assert(lower <= upper);
		g[src].emplace_back(Edge{src, dst, upper, cost * objective, re});
		g[dst].emplace_back(Edge{dst, src, -lower, -cost * objective, e});
		return EdgePtr{this, src, e};
	}

	void add_supply(const V_id v, const Flow amount) { b[v] += amount; }

	void add_demand(const V_id v, const Flow amount) { b[v] -= amount; }

private:
	static Cost constexpr unreachable = std::numeric_limits<Cost>::max();
	Cost farthest;
	std::vector<Cost> potential;
	std::vector<Cost> dist;
	std::vector<Edge *> parent;
	std::priority_queue<std::pair<Cost, int>, std::vector<std::pair<Cost, int>>, std::greater<>> pq;
	std::vector<V_id> excess_vs, deficit_vs;

	Edge &rev(const Edge &e) { return g[e.dst][e.rev]; }

	void push(Edge &e, const Flow amount) {
		e.flow += amount;
		g[e.dst][e.rev].flow -= amount;
	}

	Cost residual_cost(const V_id src, const V_id dst, const Edge &e) {
		return e.cost + potential[src] - potential[dst];
	}

	bool dual(const Flow delta) {
		dist.assign(n, unreachable);
		parent.assign(n, nullptr);
		excess_vs.erase(std::remove_if(std::begin(excess_vs), std::end(excess_vs), [&](const V_id v) { return b[v] < delta; }), std::end(excess_vs));
		deficit_vs.erase(std::remove_if(std::begin(deficit_vs), std::end(deficit_vs), [&](const V_id v) { return b[v] > -delta; }), std::end(deficit_vs));
		for(const auto v : excess_vs) pq.emplace(dist[v] = 0, v);
		farthest = 0;
		std::size_t deficit_count = 0;
		while(!pq.empty()) {
			const auto [d, u] = pq.top();
			pq.pop();
			if(dist[u] < d) continue;
			farthest = d;
			if(b[u] <= -delta) ++deficit_count;
			if(deficit_count >= deficit_vs.size()) break;
			for(auto &e : g[u]) {
				if(e.residual_cap() < delta) continue;
				const auto v = e.dst;
				const auto new_dist = d + residual_cost(u, v, e);
				if(new_dist >= dist[v]) continue;
				pq.emplace(dist[v] = new_dist, v);
				parent[v] = &e;
			}
		}
		pq = decltype(pq)();
		for(V_id v = 0; v < n; ++v) {
			potential[v] += std::min(dist[v], farthest);
		}
		return deficit_count > 0;
	}

	void primal(const Flow delta) {
		for(const auto t : deficit_vs) {
			if(dist[t] > farthest) continue;
			Flow f = -b[t];
			V_id v;
			for(v = t; parent[v] != nullptr && f >= delta; v = parent[v]->src) {
				f = std::min(f, parent[v]->residual_cap());
			}
			f = std::min(f, b[v]);
			if(f < delta) continue;
			for(v = t; parent[v] != nullptr;) {
				auto &e = *parent[v];
				push(e, f);
				const size_t u = parent[v]->src;
				parent[v] = nullptr;
				v = u;
			}
			b[t] += f;
			b[v] -= f;
		}
	}

	void saturate_negative(const Flow delta) {
		excess_vs.clear();
		deficit_vs.clear();
		for(auto &es : g)
			for(auto &e : es) {
				const Flow rcap = e.residual_cap();
				const Cost rcost = residual_cost(e.src, e.dst, e);
				if(rcost < 0 && rcap >= delta) {
					push(e, rcap);
					b[e.src] -= rcap;
					b[e.dst] += rcap;
				}
			}
		for(V_id v = 0; v < n; ++v)
			if(b[v] != 0) {
				(b[v] > 0 ? excess_vs : deficit_vs).emplace_back(v);
			}
	}

public:
	std::pair<Status, Cost> solve() {
		potential.resize(n);
		for(auto &es : g)
			for(auto &e : es) {
				const Flow rcap = e.residual_cap();
				if(rcap < 0) {
					push(e, rcap);
					b[e.src] -= rcap;
					b[e.dst] += rcap;
				}
			}

		Flow inf_flow = 1;
		for(const auto &es : g)
			for(const auto &e : es) inf_flow = std::max(inf_flow, e.residual_cap());
		Flow delta = 1;
		while(delta <= inf_flow) delta *= SCALING_FACTOR;

		for(delta /= SCALING_FACTOR; delta; delta /= SCALING_FACTOR) {
			saturate_negative(delta);
			while(dual(delta)) primal(delta);
		}

		Cost value = 0;
		for(const auto &es : g)
			for(const auto &e : es) {
				value += e.flow * e.cost;
			}
		value /= 2;

		if(excess_vs.empty() && deficit_vs.empty()) {
			return {Status::OPTIMAL, value / objective};
		} else {
			return {Status::INFEASIBLE, value / objective};
		}
	}
};

template <class Flow, class Cost>
using MaxGainFlow = MinCostFlow<Flow, Cost, Objective::MAXIMIZE>;
#line 7 "a.cpp"

void main_() {
	INT(n);
	VEC(pii, ab, n);
	V<> x;
	foa(p, ab) x.pb(p.first), x.pb(p.second);
	auto re = press(x);
	int m = SZ(re);
	MaxGainFlow<int, int> mf;
	auto s = mf.add_vertex();
	auto t = mf.add_vertex();
	mf.add_supply(s, n);
	mf.add_demand(t, n);
	mf.add_edge(s, t, 0, n, 0);
	auto lef = mf.add_vertices(n);
	auto rig = mf.add_vertices(m);
	REP(i, n) {
		mf.add_edge(s, lef[i], 0, 1, 0);
		auto [ai, bi] = ab[i];
		mf.add_edge(lef[i], rig[lb(re, ai)], 0, 1, bi);
		mf.add_edge(lef[i], rig[lb(re, bi)], 0, 1, ai);
	}
	REP(j, m) {
		mf.add_edge(rig[j], t, 0, 1, 0);
	}
	print(mf.solve().second);
}
accuracy: 1
time_ms: 210
memory_kb: 3912
final_rank: 14

code_snippet: #line 2 "library/bits/stdc++.h"

// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>

#if __cplusplus >= 201103L
#include <ccomplex>
#include <cfenv>
#include <cinttypes>
#include <cstdbool>
#include <cstdint>
#include <ctgmath>
#include <cwchar>
#include <cwctype>
#endif

// C++
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>

#if __cplusplus >= 201103L
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <system_error>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#endif
#line 2 "Source.cpp"
using namespace std;

typedef string::const_iterator State;
#define eps 1e-8L
#define MAX_MOD 1000000007LL
#define GYAKU 500000004LL
#define MOD 998244353LL
#define pb push_back
#define mp make_pair
typedef long long ll;
typedef long double ld;
#define REP(a, b) for (long long(a) = 0; (a) < (b); ++(a))
#define ALL(x) (x).begin(), (x).end()

#line 1 "library/atcoder/mincostflow.hpp"



#line 5 "library/atcoder/mincostflow.hpp"
#include <cassert>
#line 9 "library/atcoder/mincostflow.hpp"

#line 1 "library/atcoder/internal_csr.hpp"



#line 7 "library/atcoder/internal_csr.hpp"

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder


#line 1 "library/atcoder/internal_queue.hpp"



#line 5 "library/atcoder/internal_queue.hpp"

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder


#line 12 "library/atcoder/mincostflow.hpp"

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    explicit mcf_graph(int n) : _n(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(_edges.size());
        _edges.push_back({from, to, cap, 0, cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(_edges.size());
        assert(0 <= i && i < m);
        return _edges[i];
    }
    std::vector<edge> edges() { return _edges; }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        int m = int(_edges.size());
        std::vector<int> edge_idx(m);

        auto g = [&]() {
            std::vector<int> degree(_n), redge_idx(m);
            std::vector<std::pair<int, _edge>> elist;
            elist.reserve(2 * m);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] = degree[e.from]++;
                redge_idx[i] = degree[e.to]++;
                elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}});
                elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}});
            }
            auto _g = internal::csr<_edge>(_n, elist);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] += _g.start[e.from];
                redge_idx[i] += _g.start[e.to];
                _g.elist[edge_idx[i]].rev = redge_idx[i];
                _g.elist[redge_idx[i]].rev = edge_idx[i];
            }
            return _g;
        }();

        auto result = slope(g, s, t, flow_limit);

        for (int i = 0; i < m; i++) {
            auto e = g.elist[edge_idx[i]];
            _edges[i].flow = _edges[i].cap - e.cap;
        }

        return result;
    }

  private:
    int _n;
    std::vector<edge> _edges;

    // inside edge
    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
                                            int s,
                                            int t,
                                            Cap flow_limit) {
        // variants (C = maxcost):
        // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
        // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge

        // dual_dist[i] = (dual[i], dist[i])
        std::vector<std::pair<Cost, Cost>> dual_dist(_n);
        std::vector<int> prev_e(_n);
        std::vector<bool> vis(_n);
        struct Q {
            Cost key;
            int to;
            bool operator<(Q r) const { return key > r.key; }
        };
        std::vector<int> que_min;
        std::vector<Q> que;
        auto dual_ref = [&]() {
            for (int i = 0; i < _n; i++) {
                dual_dist[i].second = std::numeric_limits<Cost>::max();
            }
            std::fill(vis.begin(), vis.end(), false);
            que_min.clear();
            que.clear();

            // que[0..heap_r) was heapified
            size_t heap_r = 0;

            dual_dist[s].second = 0;
            que_min.push_back(s);
            while (!que_min.empty() || !que.empty()) {
                int v;
                if (!que_min.empty()) {
                    v = que_min.back();
                    que_min.pop_back();
                } else {
                    while (heap_r < que.size()) {
                        heap_r++;
                        std::push_heap(que.begin(), que.begin() + heap_r);
                    }
                    v = que.front().to;
                    std::pop_heap(que.begin(), que.end());
                    que.pop_back();
                    heap_r--;
                }
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                // dist[v] = shortest(s, v) + dual[s] - dual[v]
                // dist[v] >= 0 (all reduced cost are positive)
                // dist[v] <= (n-1)C
                Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                    auto e = g.elist[i];
                    if (!e.cap) continue;
                    // |-dual[e.to] + dual[v]| <= (n-1)C
                    // cost <= C - -(n-1)C + 0 = nC
                    Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                    if (dual_dist[e.to].second - dist_v > cost) {
                        Cost dist_to = dist_v + cost;
                        dual_dist[e.to].second = dist_to;
                        prev_e[e.to] = e.rev;
                        if (dist_to == dist_v) {
                            que_min.push_back(e.to);
                        } else {
                            que.push_back(Q{dist_to, e.to});
                        }
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                // dual[v] = dual[v] - dist[t] + dist[v]
                //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) +
                //         (shortest(s, v) + dual[s] - dual[v]) = - shortest(s,
                //         t) + dual[t] + shortest(s, v) = shortest(s, v) -
                //         shortest(s, t) >= 0 - (n-1)C
                dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}};
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
            }
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                auto& e = g.elist[prev_e[v]];
                e.cap += c;
                g.elist[e.rev].cap -= c;
            }
            Cost d = -dual_dist[s].first;
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }
};

}  // namespace atcoder


#line 17 "Source.cpp"
using namespace atcoder;

#define int long long
void solve()
{
  int n;
  cin >> n;
  vector<pair<int, int>> inputs;
  map<int, int> cnter;
  REP(i, n)
  {
    int a, b;
    cin >> a >> b;
    cnter[a];
    cnter[b];
    inputs.push_back({a, b});
  }

  {
    int cnt = 0;
    for (auto &x : cnter)
    {
      x.second = cnt;
      cnt++;
    }
  }
  int ans = 0;
  mcf_graph<int, int> graph(3 + inputs.size() + cnter.size());
  for (int i = 0; i < inputs.size(); ++i)
  {
    graph.add_edge(0, i + 1, 1, 0);
    graph.add_edge(i + 1, inputs.size() + 1 + cnter[inputs[i].first], 1, inputs[i].first);
    graph.add_edge(i + 1, inputs.size() + 1 + cnter[inputs[i].second], 1, inputs[i].second);
    graph.add_edge(i + 1, inputs.size() + cnter.size() + 1, 1, inputs[i].first + inputs[i].second);
    ans += inputs[i].first + inputs[i].second;
  }
  for (int i = 0; i < cnter.size(); ++i)
  {
    graph.add_edge(inputs.size() + 1 + i, inputs.size() + cnter.size() + 2, 1, 0);
  }

  graph.add_edge(inputs.size() + 1 + cnter.size(), inputs.size() + cnter.size() + 2, 10000, 0);

  ans -= graph.flow(0, inputs.size() + cnter.size() + 2).second;
  cout << ans << endl;
}
#undef int

// generated by oj-template v4.7.2
// (https://github.com/online-judge-tools/template-generator)
int main()
{
  // Fasterize input/output script
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout << fixed << setprecision(100);
  // scanf/printf user should delete this fasterize input/output script

  int t = 1;
  // cin >> t; // comment out if solving multi testcase
  for (int testCase = 1; testCase <= t; ++testCase)
  {
    solve();
  }
  return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 4176
final_rank: 3

code_snippet: #include <iostream>

#include <algorithm>
#include <cassert>
#include <vector>

#include <limits>
#include <type_traits>

namespace suisen {
// ! utility
template <typename ...Types>
using constraints_t = std::enable_if_t<std::conjunction_v<Types...>, std::nullptr_t>;
template <bool cond_v, typename Then, typename OrElse>
constexpr decltype(auto) constexpr_if(Then&& then, OrElse&& or_else) {
    if constexpr (cond_v) {
        return std::forward<Then>(then);
    } else {
        return std::forward<OrElse>(or_else);
    }
}

// ! function
template <typename ReturnType, typename Callable, typename ...Args>
using is_same_as_invoke_result = std::is_same<std::invoke_result_t<Callable, Args...>, ReturnType>;
template <typename F, typename T>
using is_uni_op = is_same_as_invoke_result<T, F, T>;
template <typename F, typename T>
using is_bin_op = is_same_as_invoke_result<T, F, T, T>;

template <typename Comparator, typename T>
using is_comparator = std::is_same<std::invoke_result_t<Comparator, T, T>, bool>;

// ! integral
template <typename T, typename = constraints_t<std::is_integral<T>>>
constexpr int bit_num = std::numeric_limits<std::make_unsigned_t<T>>::digits;
template <typename T, unsigned int n>
struct is_nbit { static constexpr bool value = bit_num<T> == n; };
template <typename T, unsigned int n>
static constexpr bool is_nbit_v = is_nbit<T, n>::value;

// ?
template <typename T>
struct safely_multipliable {};
template <>
struct safely_multipliable<int> { using type = long long; };
template <>
struct safely_multipliable<long long> { using type = __int128_t; };
template <>
struct safely_multipliable<float> { using type = float; };
template <>
struct safely_multipliable<double> { using type = double; };
template <>
struct safely_multipliable<long double> { using type = long double; };
template <typename T>
using safely_multipliable_t = typename safely_multipliable<T>::type;

} // namespace suisen

namespace suisen {
template <typename T>
class CoordinateCompressorBuilder {
    public:
        struct Compressor {
            public:
                static constexpr int absent = -1;

                // default constructor
                Compressor() : _xs(std::vector<T>{}) {}
                // Construct from strictly sorted vector
                Compressor(const std::vector<T> &xs) : _xs(xs) {
                    assert(is_strictly_sorted(xs));
                }

                // Return the number of distinct keys.
                int size() const {
                    return _xs.size();
                }
                // Check if the element is registered.
                bool has_key(const T &e) const {
                    return std::binary_search(_xs.begin(), _xs.end(), e);
                }
                // Compress the element. if not registered, returns `default_value`. (default: Compressor::absent)
                int comp(const T &e, int default_value = absent) const {
                    const int res = min_geq_index(e);
                    return res != size() and _xs[res] == e ? res : default_value;
                }
                // Restore the element from the index.
                T decomp(const int compressed_index) const {
                    return _xs[compressed_index];
                }
                // Compress the element. Equivalent to call `comp(e)`
                int operator[](const T &e) const {
                    return comp(e);
                }
                // Return the minimum registered value greater than `e`. if not exists, return `default_value`.
                T min_gt(const T &e, const T &default_value) const {
                    auto it = std::upper_bound(_xs.begin(), _xs.end(), e);
                    return it == _xs.end() ? default_value : *it;
                }
                // Return the minimum registered value greater than or equal to `e`. if not exists, return `default_value`.
                T min_geq(const T &e, const T &default_value) const {
                    auto it = std::lower_bound(_xs.begin(), _xs.end(), e);
                    return it == _xs.end() ? default_value : *it;
                }
                // Return the maximum registered value less than `e`. if not exists, return `default_value`
                T max_lt(const T &e, const T &default_value) const {
                    auto it = std::upper_bound(_xs.rbegin(), _xs.rend(), e);
                    return it == _xs.rend() ? default_value : *it;
                }
                // Return the maximum registered value less than or equal to `e`. if not exists, return `default_value`
                T max_leq(const T &e, const T &default_value) const {
                    auto it = std::lower_bound(_xs.rbegin(), _xs.rend(), e);
                    return it == _xs.rend() ? default_value : *it;
                }
                // Return the compressed index of the minimum registered value greater than `e`. if not exists, return `compressor.size()`.
                int min_gt_index(const T &e) const {
                    return std::upper_bound(_xs.begin(), _xs.end(), e) - _xs.begin();
                }
                // Return the compressed index of the minimum registered value greater than or equal to `e`. if not exists, return `compressor.size()`.
                int min_geq_index(const T &e) const {
                    return std::lower_bound(_xs.begin(), _xs.end(), e) - _xs.begin();
                }
                // Return the compressed index of the maximum registered value less than `e`. if not exists, return -1.
                int max_lt_index(const T &e) const {
                    return int(_xs.rend() - std::upper_bound(_xs.rbegin(), _xs.rend(), e)) - 1;
                }
                // Return the compressed index of the maximum registered value less than or equal to `e`. if not exists, return -1.
                int max_leq_index(const T &e) const {
                    return int(_xs.rend() - std::lower_bound(_xs.rbegin(), _xs.rend(), e)) - 1;
                }
            private:
                std::vector<T> _xs;
                static bool is_strictly_sorted(const std::vector<T> &v) {
                    return std::adjacent_find(v.begin(), v.end(), std::greater_equal<T>()) == v.end();
                }
        };
        CoordinateCompressorBuilder() : _xs(std::vector<T>{}) {}
        explicit CoordinateCompressorBuilder(const std::vector<T> &xs) : _xs(xs) {}
        explicit CoordinateCompressorBuilder(std::vector<T> &&xs) : _xs(std::move(xs)) {}
        template <typename Gen, constraints_t<is_same_as_invoke_result<T, Gen, int>> = nullptr>
        CoordinateCompressorBuilder(const int n, Gen generator) {
            reserve(n);
            for (int i = 0; i < n; ++i) push(generator(i));
        }
        // Attempt to preallocate enough memory for specified number of elements.
        void reserve(int n) {
            _xs.reserve(n);
        }
        // Add data.
        void push(const T &first) {
            _xs.push_back(first);
        }
        // Add data.
        void push(T &&first) {
            _xs.push_back(std::move(first));
        }
        // Add data in the range of [first, last). 
        template <typename Iterator>
        auto push(const Iterator &first, const Iterator &last) -> decltype(std::vector<T>{}.push_back(*first), void()) {
            for (auto it = first; it != last; ++it) _xs.push_back(*it);
        }
        // Add all data in the container. Equivalent to `push(iterable.begin(), iterable.end())`.
        template <typename Iterable>
        auto push(const Iterable &iterable) -> decltype(std::vector<T>{}.push_back(*iterable.begin()), void()) {
            push(iterable.begin(), iterable.end());
        }
        // Add data.
        template <typename ...Args>
        void emplace(Args &&...args) {
            _xs.emplace_back(std::forward<Args>(args)...);
        }
        // Build compressor.
        auto build() {
            std::sort(_xs.begin(), _xs.end()), _xs.erase(std::unique(_xs.begin(), _xs.end()), _xs.end());
            return Compressor {_xs};
        }
        // Build compressor from vector.
        static auto build(const std::vector<T> &xs) {
            return CoordinateCompressorBuilder(xs).build();
        }
        // Build compressor from vector.
        static auto build(std::vector<T> &&xs) {
            return CoordinateCompressorBuilder(std::move(xs)).build();
        }
        // Build compressor from generator.
        template <typename Gen, constraints_t<is_same_as_invoke_result<T, Gen, int>> = nullptr>
        static auto build(const int n, Gen generator) {
            return CoordinateCompressorBuilder<T>(n, generator).build();
        }
    private:
        std::vector<T> _xs;
};

} // namespace suisen

#include <queue>

namespace suisen {

enum MCFPotentialInitializer {
    DAG, BELLMAN_FORD, DIJKSTRA
};

template <typename Cap, typename Cost, MCFPotentialInitializer init_method = MCFPotentialInitializer::BELLMAN_FORD>
class MinCostFlow {
    struct InternalEdge { int to; Cap cap; Cost cost; int rev; };
    public:
        MinCostFlow() : MinCostFlow(0) {}
        MinCostFlow(int n) : n(n), g(n), potential(n, 0), dist(n), prev_vid(n), prev_eid(n) {}

        // Returns the id of created edge.
        int add_edge(int u, int v, Cap cap, Cost cost) {
            int edge_id = edges.size();
            edges.emplace_back(u, g[u].size());
            g[u].push_back({ v, cap,  cost, int(g[v].size())     });
            g[v].push_back({ u,   0, -cost, int(g[u].size()) - 1 });
            return edge_id;
        }

        /**
         * Returns { flow, cost } (flow = min(max_flow, f))
         */
        auto min_cost_max_flow(const int s, const int t, const Cap f) {
            return min_cost_max_flow_slope(s, t, f).back();
        }
        /**
         * Returns { flow, cost } (flow = max_flow)
         */
        auto min_cost_max_flow(const int s, const int t) {
            return min_cost_max_flow_slope(s, t).back();
        }
        /**
         * Returns { flow, cost }
         * amount of flow is arbitrary.
         */
        auto min_cost_arbitrary_flow(const int s, const int t) {
            return min_cost_arbitrary_flow_slope(s, t).back();
        }

        auto min_cost_max_flow_slope(const int s, const int t, const Cap f) {
            return min_cost_flow_slope(s, t, f, [](Cap, Cost){ return true; });
        }
        auto min_cost_max_flow_slope(const int s, const int t) {
            return min_cost_max_flow_slope(s, t, INF_FLOW);
        }
        auto min_cost_arbitrary_flow_slope(const int s, const int t) {
            return min_cost_flow_slope(s, t, INF_FLOW, [this, t](Cap, Cost){ return potential[t] < 0; });
        }

        struct Edge {
            int from, to;
            Cap cap, flow;
            Cost cost;
        };

        Edge get_edge(int edge_id) const {
            const auto &e  = g[edges[edge_id].first][edges[edge_id].second];
            const auto &re = g[e.to][e.rev];
            return Edge { re.to, e.to, e.cap + re.cap, re.cap, e.cost };
        }
        std::vector<Edge> get_edges() const {
            std::vector<Edge> res(edges.size());
            for (std::size_t i = 0; i < edges.size(); ++i) res[i] = get_edge(i);
            return res;
        }

    private:
        static constexpr Cost INF_COST = std::numeric_limits<Cost>::max() / 2;
        static constexpr Cost INF_FLOW = std::numeric_limits<Cap>::max() / 2;
    
        int n;
        std::vector<std::vector<InternalEdge>> g;
        std::vector<Cost> potential;
        std::vector<Cost> dist;
        std::vector<int> prev_vid, prev_eid;

        std::vector<std::pair<int, int>> edges;

        template <typename Predicate>
        std::pair<Cap, Cost> min_cost_flow(const int s, const int t, const Cap upper_flow, Predicate pred) {
            return min_cost_flow_slope(s, t, upper_flow, pred).back();
        }

        template <typename Predicate>
        std::vector<std::pair<Cap, Cost>> min_cost_flow_slope(const int s, const int t, const Cap upper_flow, Predicate pred) {
            if constexpr (init_method == BELLMAN_FORD) {
                bellman_ford(s);
            } else if constexpr (init_method == DIJKSTRA) {
                dijkstra(s);
            } else {
                dag_dp(s);
            }
            update_potential();
            std::vector<std::pair<Cap, Cost>> slope;
            Cap flow = 0;
            Cost cost = 0;
            slope.emplace_back(flow, cost);
            while (dist[t] != INF_COST and flow < upper_flow and pred(flow, cost)) {
                Cap df = upper_flow - flow;
                for (int v = t; v != s; v = prev_vid[v]) {
                    df = std::min(df, g[prev_vid[v]][prev_eid[v]].cap);
                }
                assert(df != 0);
                flow += df;
                cost += df * potential[t];
                if (slope.size() >= 2) {
                    auto [f0, c0] = *std::next(slope.rbegin());
                    auto [f1, c1] = *slope.rbegin();
                    if ((f1 - f0) * (cost - c1) == (flow - f1) * (c1 - c0)) slope.pop_back();
                }
                slope.emplace_back(flow, cost);
                for (int v = t; v != s; v = prev_vid[v]) {
                    auto &e = g[prev_vid[v]][prev_eid[v]];
                    e.cap -= df;
                    g[v][e.rev].cap += df;
                }
                dijkstra(s);
                update_potential();
            }
            return slope;
        }

        void update_potential() {
            for (int u = 0; u < n; ++u) {
                if (potential[u] != INF_COST) potential[u] += dist[u];
            }
        }

        bool update_dist(int u, int eid) {
            if (dist[u] == INF_COST) return false;
            const auto &e = g[u][eid];
            if (e.cap == 0) return false;
            const int v = e.to;
            Cost cost = e.cost + potential[u] - potential[v];
            if constexpr (init_method == DIJKSTRA) {
                assert(cost >= 0);
            }
            if (dist[u] + cost < dist[v]) {
                dist[v] = dist[u] + cost;
                prev_vid[v] = u;
                prev_eid[v] = eid;
                return true;
            }
            return false;
        }

        void dijkstra(int s) {
            using State = std::pair<Cost, int>;
            std::priority_queue<State, std::vector<State>, std::greater<State>> pq;
            dist.assign(n, INF_COST);
            pq.emplace(dist[s] = 0, s);
            while (pq.size()) {
                auto [du, u] = pq.top();
                pq.pop();
                if (du != dist[u]) continue;
                for (int i = 0; i < int(g[u].size()); ++i) {
                    int v = g[u][i].to;
                    if (update_dist(u, i)) {
                        pq.emplace(dist[v], v);
                    }
                }
            }
        }

        void dag_dp(int s) {
            std::vector<int> in(n, 0);
            for (int u = 0; u < n; ++u) {
                for (const auto &e : g[u]) {
                    if (e.cap == 0) continue;
                    ++in[e.to];
                }
            }
            std::deque<int> dq;
            for (int u = 0; u < n; ++u) {
                if (in[u] == 0) dq.push_back(u);
            }
            dist.assign(n, INF_COST);
            dist[s] = 0;
            while (dq.size()) {
                int u = dq.front();
                dq.pop_front();
                for (int i = 0; i < int(g[u].size()); ++i) {
                    const auto &e = g[u][i];
                    if (e.cap == 0) continue;
                    update_dist(u, i);
                    if (--in[e.to] == 0) {
                        dq.push_back(e.to);
                    }
                }
            }
            assert(std::all_of(in.begin(), in.end(), [](int in_deg) { return in_deg == 0; }));
        }

        void bellman_ford(int s) {
            dist.assign(n, INF_COST);
            dist[s] = 0;
            for (bool has_update = true; has_update;) {
                has_update = false;
                for (int u = 0; u < n; ++u) {
                    if (dist[u] == INF_COST) continue;
                    for (int i = 0; i < int(g[u].size()); ++i) {
                        has_update |= update_dist(u, i);
                    }
                }
            }
        }
};
} // namespace suisen

int main() {
    int n;
    std::cin >> n;
    suisen::CoordinateCompressorBuilder<int> comp_builder;
    std::vector<std::pair<int, int>> ps(n);
    for (auto &[x, y] : ps) {
        std::cin >> x >> y;
        comp_builder.push(x);
        comp_builder.push(y);
    }
    auto comp = comp_builder.build();
    int m = comp.size();
    int s = n + m, t = s + 1;

    suisen::MinCostFlow<int, int, suisen::DAG> mcf(n + m + 2);
    for (int i = 0; i < n; ++i) {
        mcf.add_edge(s, i, 1, 0);
        auto [a, b] = ps[i];
        mcf.add_edge(i, n + comp[a], 1, -b);
        mcf.add_edge(i, n + comp[b], 1, -a);
    }
    for (int i = 0; i < m; ++i) {
        mcf.add_edge(n + i, t, 1, 0);
    }
    std::cout << -mcf.min_cost_arbitrary_flow(s, t).second << std::endl;
    return 0;
}
accuracy: 1
time_ms: 190
memory_kb: 3832
final_rank: 10

code_snippet: #include <iostream>

#include <algorithm>
#include <cassert>
#include <vector>

#include <limits>
#include <type_traits>

namespace suisen {
// ! utility
template <typename ...Types>
using constraints_t = std::enable_if_t<std::conjunction_v<Types...>, std::nullptr_t>;
template <bool cond_v, typename Then, typename OrElse>
constexpr decltype(auto) constexpr_if(Then&& then, OrElse&& or_else) {
    if constexpr (cond_v) {
        return std::forward<Then>(then);
    } else {
        return std::forward<OrElse>(or_else);
    }
}

// ! function
template <typename ReturnType, typename Callable, typename ...Args>
using is_same_as_invoke_result = std::is_same<std::invoke_result_t<Callable, Args...>, ReturnType>;
template <typename F, typename T>
using is_uni_op = is_same_as_invoke_result<T, F, T>;
template <typename F, typename T>
using is_bin_op = is_same_as_invoke_result<T, F, T, T>;

template <typename Comparator, typename T>
using is_comparator = std::is_same<std::invoke_result_t<Comparator, T, T>, bool>;

// ! integral
template <typename T, typename = constraints_t<std::is_integral<T>>>
constexpr int bit_num = std::numeric_limits<std::make_unsigned_t<T>>::digits;
template <typename T, unsigned int n>
struct is_nbit { static constexpr bool value = bit_num<T> == n; };
template <typename T, unsigned int n>
static constexpr bool is_nbit_v = is_nbit<T, n>::value;

// ?
template <typename T>
struct safely_multipliable {};
template <>
struct safely_multipliable<int> { using type = long long; };
template <>
struct safely_multipliable<long long> { using type = __int128_t; };
template <>
struct safely_multipliable<float> { using type = float; };
template <>
struct safely_multipliable<double> { using type = double; };
template <>
struct safely_multipliable<long double> { using type = long double; };
template <typename T>
using safely_multipliable_t = typename safely_multipliable<T>::type;

} // namespace suisen

namespace suisen {
template <typename T>
class CoordinateCompressorBuilder {
    public:
        struct Compressor {
            public:
                static constexpr int absent = -1;

                // default constructor
                Compressor() : _xs(std::vector<T>{}) {}
                // Construct from strictly sorted vector
                Compressor(const std::vector<T> &xs) : _xs(xs) {
                    assert(is_strictly_sorted(xs));
                }

                // Return the number of distinct keys.
                int size() const {
                    return _xs.size();
                }
                // Check if the element is registered.
                bool has_key(const T &e) const {
                    return std::binary_search(_xs.begin(), _xs.end(), e);
                }
                // Compress the element. if not registered, returns `default_value`. (default: Compressor::absent)
                int comp(const T &e, int default_value = absent) const {
                    const int res = min_geq_index(e);
                    return res != size() and _xs[res] == e ? res : default_value;
                }
                // Restore the element from the index.
                T decomp(const int compressed_index) const {
                    return _xs[compressed_index];
                }
                // Compress the element. Equivalent to call `comp(e)`
                int operator[](const T &e) const {
                    return comp(e);
                }
                // Return the minimum registered value greater than `e`. if not exists, return `default_value`.
                T min_gt(const T &e, const T &default_value) const {
                    auto it = std::upper_bound(_xs.begin(), _xs.end(), e);
                    return it == _xs.end() ? default_value : *it;
                }
                // Return the minimum registered value greater than or equal to `e`. if not exists, return `default_value`.
                T min_geq(const T &e, const T &default_value) const {
                    auto it = std::lower_bound(_xs.begin(), _xs.end(), e);
                    return it == _xs.end() ? default_value : *it;
                }
                // Return the maximum registered value less than `e`. if not exists, return `default_value`
                T max_lt(const T &e, const T &default_value) const {
                    auto it = std::upper_bound(_xs.rbegin(), _xs.rend(), e);
                    return it == _xs.rend() ? default_value : *it;
                }
                // Return the maximum registered value less than or equal to `e`. if not exists, return `default_value`
                T max_leq(const T &e, const T &default_value) const {
                    auto it = std::lower_bound(_xs.rbegin(), _xs.rend(), e);
                    return it == _xs.rend() ? default_value : *it;
                }
                // Return the compressed index of the minimum registered value greater than `e`. if not exists, return `compressor.size()`.
                int min_gt_index(const T &e) const {
                    return std::upper_bound(_xs.begin(), _xs.end(), e) - _xs.begin();
                }
                // Return the compressed index of the minimum registered value greater than or equal to `e`. if not exists, return `compressor.size()`.
                int min_geq_index(const T &e) const {
                    return std::lower_bound(_xs.begin(), _xs.end(), e) - _xs.begin();
                }
                // Return the compressed index of the maximum registered value less than `e`. if not exists, return -1.
                int max_lt_index(const T &e) const {
                    return int(_xs.rend() - std::upper_bound(_xs.rbegin(), _xs.rend(), e)) - 1;
                }
                // Return the compressed index of the maximum registered value less than or equal to `e`. if not exists, return -1.
                int max_leq_index(const T &e) const {
                    return int(_xs.rend() - std::lower_bound(_xs.rbegin(), _xs.rend(), e)) - 1;
                }
            private:
                std::vector<T> _xs;
                static bool is_strictly_sorted(const std::vector<T> &v) {
                    return std::adjacent_find(v.begin(), v.end(), std::greater_equal<T>()) == v.end();
                }
        };
        CoordinateCompressorBuilder() : _xs(std::vector<T>{}) {}
        explicit CoordinateCompressorBuilder(const std::vector<T> &xs) : _xs(xs) {}
        explicit CoordinateCompressorBuilder(std::vector<T> &&xs) : _xs(std::move(xs)) {}
        template <typename Gen, constraints_t<is_same_as_invoke_result<T, Gen, int>> = nullptr>
        CoordinateCompressorBuilder(const int n, Gen generator) {
            reserve(n);
            for (int i = 0; i < n; ++i) push(generator(i));
        }
        // Attempt to preallocate enough memory for specified number of elements.
        void reserve(int n) {
            _xs.reserve(n);
        }
        // Add data.
        void push(const T &first) {
            _xs.push_back(first);
        }
        // Add data.
        void push(T &&first) {
            _xs.push_back(std::move(first));
        }
        // Add data in the range of [first, last). 
        template <typename Iterator>
        auto push(const Iterator &first, const Iterator &last) -> decltype(std::vector<T>{}.push_back(*first), void()) {
            for (auto it = first; it != last; ++it) _xs.push_back(*it);
        }
        // Add all data in the container. Equivalent to `push(iterable.begin(), iterable.end())`.
        template <typename Iterable>
        auto push(const Iterable &iterable) -> decltype(std::vector<T>{}.push_back(*iterable.begin()), void()) {
            push(iterable.begin(), iterable.end());
        }
        // Add data.
        template <typename ...Args>
        void emplace(Args &&...args) {
            _xs.emplace_back(std::forward<Args>(args)...);
        }
        // Build compressor.
        auto build() {
            std::sort(_xs.begin(), _xs.end()), _xs.erase(std::unique(_xs.begin(), _xs.end()), _xs.end());
            return Compressor {_xs};
        }
        // Build compressor from vector.
        static auto build(const std::vector<T> &xs) {
            return CoordinateCompressorBuilder(xs).build();
        }
        // Build compressor from vector.
        static auto build(std::vector<T> &&xs) {
            return CoordinateCompressorBuilder(std::move(xs)).build();
        }
        // Build compressor from generator.
        template <typename Gen, constraints_t<is_same_as_invoke_result<T, Gen, int>> = nullptr>
        static auto build(const int n, Gen generator) {
            return CoordinateCompressorBuilder<T>(n, generator).build();
        }
    private:
        std::vector<T> _xs;
};

} // namespace suisen

#include <queue>

namespace suisen {

enum MCFPotentialInitializer {
    DAG, BELLMAN_FORD, DIJKSTRA
};

template <typename Cap, typename Cost, MCFPotentialInitializer init_method = MCFPotentialInitializer::BELLMAN_FORD>
class MinCostFlow {
    struct InternalEdge { int to; Cap cap; Cost cost; int rev; };
    public:
        MinCostFlow() : MinCostFlow(0) {}
        MinCostFlow(int n) : n(n), g(n), potential(n, 0), dist(n), prev_vid(n), prev_eid(n) {}

        // Returns the id of created edge.
        int add_edge(int u, int v, Cap cap, Cost cost) {
            int edge_id = edges.size();
            edges.emplace_back(u, g[u].size());
            g[u].push_back({ v, cap,  cost, int(g[v].size())     });
            g[v].push_back({ u,   0, -cost, int(g[u].size()) - 1 });
            return edge_id;
        }

        /**
         * Returns { flow, cost } (flow = min(max_flow, f))
         */
        auto min_cost_max_flow(const int s, const int t, const Cap f) {
            return min_cost_max_flow_slope(s, t, f).back();
        }
        /**
         * Returns { flow, cost } (flow = max_flow)
         */
        auto min_cost_max_flow(const int s, const int t) {
            return min_cost_max_flow_slope(s, t).back();
        }
        /**
         * Returns { flow, cost }
         * amount of flow is arbitrary.
         */
        auto min_cost_arbitrary_flow(const int s, const int t) {
            return min_cost_arbitrary_flow_slope(s, t).back();
        }

        auto min_cost_max_flow_slope(const int s, const int t, const Cap f) {
            return min_cost_flow_slope(s, t, f, [](Cap, Cost){ return true; });
        }
        auto min_cost_max_flow_slope(const int s, const int t) {
            return min_cost_max_flow_slope(s, t, INF_FLOW);
        }
        auto min_cost_arbitrary_flow_slope(const int s, const int t) {
            return min_cost_flow_slope(s, t, INF_FLOW, [this, t](Cap, Cost){ return potential[t] < 0; });
        }

        struct Edge {
            int from, to;
            Cap cap, flow;
            Cost cost;
        };

        Edge get_edge(int edge_id) const {
            const auto &e  = g[edges[edge_id].first][edges[edge_id].second];
            const auto &re = g[e.to][e.rev];
            return Edge { re.to, e.to, e.cap + re.cap, re.cap, e.cost };
        }
        std::vector<Edge> get_edges() const {
            std::vector<Edge> res(edges.size());
            for (std::size_t i = 0; i < edges.size(); ++i) res[i] = get_edge(i);
            return res;
        }

    private:
        static constexpr Cost INF_COST = std::numeric_limits<Cost>::max() / 2;
        static constexpr Cost INF_FLOW = std::numeric_limits<Cap>::max() / 2;
    
        int n;
        std::vector<std::vector<InternalEdge>> g;
        std::vector<Cost> potential;
        std::vector<Cost> dist;
        std::vector<int> prev_vid, prev_eid;

        std::vector<std::pair<int, int>> edges;

        template <typename Predicate>
        std::pair<Cap, Cost> min_cost_flow(const int s, const int t, const Cap upper_flow, Predicate pred) {
            return min_cost_flow_slope(s, t, upper_flow, pred).back();
        }

        template <typename Predicate>
        std::vector<std::pair<Cap, Cost>> min_cost_flow_slope(const int s, const int t, const Cap upper_flow, Predicate pred) {
            switch (init_method) {
                case BELLMAN_FORD: bellman_ford(s); break;
                case DIJKSTRA:     dijkstra(s);     break;
                case DAG:          dag_dp(s);       break;
            }
            update_potential();
            std::vector<std::pair<Cap, Cost>> slope;
            Cap flow = 0;
            Cost cost = 0;
            slope.emplace_back(flow, cost);
            while (dist[t] != INF_COST and flow < upper_flow and pred(flow, cost)) {
                Cap df = upper_flow - flow;
                for (int v = t; v != s; v = prev_vid[v]) {
                    df = std::min(df, g[prev_vid[v]][prev_eid[v]].cap);
                }
                assert(df != 0);
                flow += df;
                cost += df * potential[t];
                if (slope.size() >= 2) {
                    auto [f0, c0] = *std::next(slope.rbegin());
                    auto [f1, c1] = *slope.rbegin();
                    if ((f1 - f0) * (cost - c1) == (flow - f1) * (c1 - c0)) slope.pop_back();
                }
                slope.emplace_back(flow, cost);
                for (int v = t; v != s; v = prev_vid[v]) {
                    auto &e = g[prev_vid[v]][prev_eid[v]];
                    e.cap -= df;
                    g[v][e.rev].cap += df;
                }
                dijkstra(s);
                update_potential();
            }
            return slope;
        }

        void update_potential() {
            for (int u = 0; u < n; ++u) {
                if (potential[u] != INF_COST) potential[u] += dist[u];
            }
        }

        bool update_dist(int u, int eid) {
            if (dist[u] == INF_COST) return false;
            const auto &e = g[u][eid];
            if (e.cap == 0) return false;
            const int v = e.to;
            Cost cost = e.cost + potential[u] - potential[v];
            if constexpr (init_method == DIJKSTRA) {
                assert(cost >= 0);
            }
            if (dist[u] + cost < dist[v]) {
                dist[v] = dist[u] + cost;
                prev_vid[v] = u;
                prev_eid[v] = eid;
                return true;
            }
            return false;
        }

        void dijkstra(int s) {
            using State = std::pair<Cost, int>;
            std::priority_queue<State, std::vector<State>, std::greater<State>> pq;
            dist.assign(n, INF_COST);
            pq.emplace(dist[s] = 0, s);
            while (pq.size()) {
                auto [du, u] = pq.top();
                pq.pop();
                if (du != dist[u]) continue;
                for (int i = 0; i < int(g[u].size()); ++i) {
                    int v = g[u][i].to;
                    if (update_dist(u, i)) {
                        pq.emplace(dist[v], v);
                    }
                }
            }
        }

        void dag_dp(int s) {
            std::vector<int> in(n, 0);
            for (int u = 0; u < n; ++u) {
                for (const auto &e : g[u]) {
                    if (e.cap == 0) continue;
                    ++in[e.to];
                }
            }
            std::deque<int> dq;
            for (int u = 0; u < n; ++u) {
                if (in[u] == 0) dq.push_back(u);
            }
            dist.assign(n, INF_COST);
            dist[s] = 0;
            while (dq.size()) {
                int u = dq.front();
                dq.pop_front();
                for (int i = 0; i < int(g[u].size()); ++i) {
                    const auto &e = g[u][i];
                    if (e.cap == 0) continue;
                    update_dist(u, i);
                    if (--in[e.to] == 0) {
                        dq.push_back(e.to);
                    }
                }
            }
            assert(std::all_of(in.begin(), in.end(), [](int in_deg) { return in_deg == 0; }));
        }

        void bellman_ford(int s) {
            dist.assign(n, INF_COST);
            dist[s] = 0;
            for (bool has_update = true; has_update;) {
                has_update = false;
                for (int u = 0; u < n; ++u) {
                    if (dist[u] == INF_COST) continue;
                    for (int i = 0; i < int(g[u].size()); ++i) {
                        has_update |= update_dist(u, i);
                    }
                }
            }
        }
};
} // namespace suisen

int main() {
    int n;
    std::cin >> n;
    suisen::CoordinateCompressorBuilder<int> comp_builder;
    std::vector<std::pair<int, int>> ps(n);
    for (auto &[x, y] : ps) {
        std::cin >> x >> y;
        comp_builder.push(x);
        comp_builder.push(y);
    }
    auto comp = comp_builder.build();
    int m = comp.size();
    int s = n + m, t = s + 1;

    suisen::MinCostFlow<int, int, suisen::DAG> mcf(n + m + 2);
    for (int i = 0; i < n; ++i) {
        mcf.add_edge(s, i, 1, 0);
        auto [a, b] = ps[i];
        mcf.add_edge(i, n + comp[a], 1, -b);
        mcf.add_edge(i, n + comp[b], 1, -a);
    }
    for (int i = 0; i < m; ++i) {
        mcf.add_edge(n + i, t, 1, 0);
    }
    std::cout << -mcf.min_cost_arbitrary_flow(s, t).second << std::endl;
    return 0;
}
accuracy: 1
time_ms: 190
memory_kb: 3864
final_rank: 11