#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>

using namespace std;

void failure () {
  cout << 0 << endl;
  exit(0);
}

typedef long long ll;

const int MOD = 1e9 + 7; // change if needed

struct Modint {
  ll val;
  
  Modint (ll _val = 0)
    : val(_val % MOD) {}

  Modint operator+ (Modint other) const {
    return Modint(val + other.val);
  }

  void operator+= (Modint other) {
    val += other.val;
    val %= MOD;
  }

  Modint operator- () const {
    return Modint(MOD - val);
  }

  Modint operator- (Modint other) const {
    return Modint(val + MOD - other.val);
  }

  void operator-= (Modint other) {
    val += MOD - other.val;
    val %= MOD;
  }

  Modint operator* (Modint other) const {
    return Modint(val * other.val);
  }

  void operator*= (Modint other) {
    val *= other.val;
    val %= MOD;
  }

  bool operator== (Modint other) const {
    return val == other.val;
  }

  bool operator!= (Modint other) const {
    return val != other.val;
  }
};

Modint exp (Modint a, int k) {
  if (k == 0) {
    return Modint(1);
  } else if (k % 2 == 0) {
    Modint half = exp(a, k / 2);
    return half * half;
  } else {
    return a * exp(a, k - 1);
  }
}

Modint inv (Modint a) {
  return exp(a, MOD - 2);
}

ostream& operator<< (ostream& out, Modint p) {
  out << p.val;
  return out;
}

const int MAX_N = 2e5 + 10;
const int Y = 1e5 + 5;

Modint fact [MAX_N];
Modint invfact [MAX_N];

Modint choose (int n, int k) {
  return fact[n] * invfact[n - k] * invfact[k];
}

int x_idx (int x) {
  return x;
}

int y_idx (int y) {
  return Y + y;
}

vector<int> adj [MAX_N];

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

int vis [MAX_N];

void visit_comp (int u, vector<int> &comp) {
  comp.push_back(u);
  vis[u] = 1;
  for (int nxt : adj[u]) {
    if (!vis[nxt]) {
      visit_comp(nxt, comp);
    }
  }
}

int find_cycle_vis [MAX_N];
bool find_cycle (int u, int p, deque<int> &cycle) {
  find_cycle_vis[u] = 1;
  cycle.push_back(u);
  
  for (int nxt : adj[u]) {
    if (nxt != p) {
      if (find_cycle_vis[nxt]) {
        while (cycle.front() != nxt) {
          cycle.pop_front();
        }
        return true;
      } else {
        if (find_cycle(nxt, u, cycle)) {
          return true;
        }
      }
    }
  }

  cycle.pop_back();
  return false;
}

bool on_cycle [MAX_N];
int parent [MAX_N];

void build_parent (int u, int p) {
  parent[u] = p;
  for (int nxt : adj[u]) {
    if (nxt != p && !on_cycle[nxt]) {
      build_parent(nxt, u);
    }
  }
}

vector<int> temp_adj [MAX_N];
bool is_root [MAX_N];
int sub_sz [MAX_N];
Modint sub_ways [MAX_N];

void count_sub (int u) {
  sub_ways[u] = Modint(1);
  sub_sz[u] = 0;
  for (int nxt : temp_adj[u]) {
    count_sub(nxt);
    
    sub_ways[u] *= sub_ways[nxt] * choose(sub_sz[u] + sub_sz[nxt], sub_sz[nxt]);
    sub_sz[u] += sub_sz[nxt];
  }
  sub_sz[u]++;
}

Modint count_forest (const vector<int> &comp) {
  // for each vertex, only include the children who have an index < than the parent
  for (int u : comp) {
    temp_adj[u].clear();
    is_root[u] = true;
  }

  for (int u : comp) {
    for (int nxt : adj[u]) {
      if (nxt < parent[u]) {
        temp_adj[u].push_back(nxt);
        is_root[nxt] = false;
      }
    }
  }

  Modint ans (1);
  int cur_k = 0;
  for (int u : comp) {
    if (is_root[u]) {
      count_sub(u);

      ans *= sub_ways[u] * choose(cur_k + sub_sz[u], sub_sz[u]);
      cur_k += sub_sz[u];
    }
  }

  return ans;
}

int n_seen;
Modint tot_ans;

void process_component (int u) {
  vector<int> comp;
  visit_comp(u, comp);

  int k = (int) comp.size();
  int ec = 0;
  for (int v : comp) {
    ec += (int) adj[v].size();
  }
  ec /= 2;

  if (ec != k) {
    failure();
  }

  // now, find the cycle
  deque<int> cycle;
  find_cycle(u, u, cycle);

  for (int v : cycle) {
    on_cycle[v] = true;
  }

  for (int v : cycle) {
    build_parent(v, v);
  }

  for (int i = 0; i < (int) cycle.size(); i++) {
    parent[cycle[i]] = cycle[(i + 1) % (int) cycle.size()];
  }
  Modint cur_ans = count_forest(comp);

  reverse(cycle.begin(), cycle.end());

  for (int i = 0; i < (int) cycle.size(); i++) {
    parent[cycle[i]] = cycle[(i + 1) % (int) cycle.size()];
  }
  cur_ans += count_forest(comp);

  int cur_n = (int) comp.size();
  tot_ans *= cur_ans * choose(n_seen + cur_n, cur_n);
  n_seen += cur_n;
}

int main () {
  ios::sync_with_stdio(false);

  fact[0] = Modint(1);
  invfact[0] = Modint(1);
  for (int i = 1; i < MAX_N; i++) {
    fact[i] = fact[i - 1] * Modint(i);
    invfact[i] = invfact[i - 1] * inv(Modint(i));
  }
  
  int n;
  cin >> n;

  for (int i = 0; i < 2 * n; i++) {
    int x, y;
    cin >> x >> y;

    add_edge(x_idx(x), y_idx(y));
  }
  
  tot_ans = Modint(1);
  for (int i = 1; i <= n; i++) {
    if (!vis[x_idx(i)]) {
      process_component(x_idx(i));
    }
  }

  for (int i = 1; i <= n; i++) {
    if (!vis[y_idx(i)]) {
      process_component(y_idx(i));
    }
  }

  cout << tot_ans << endl;
}


Measured memory_bytes: 31224