#include <bits/stdc++.h>
#define pct puts("------ visit ------");
#define rep(a, b, c) for(int a = (b); a <= (c); ++ a)
#define per(a, b, c) for(int a = (b); a >= (c); -- a)
using namespace std;
template <class T> void rd(T &x){
  x = 0; int f = 1; char ch = getchar();
  while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); }
  while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
  x *= f;
}

typedef long long ll;
const int mod = 1e9 + 7;
const int maxn = 2e5 + 5;
int n, sdu, tim = 2;
int mrk[maxn], to[maxn], siz[maxn];
int p[maxn], cntp;
int r[maxn], cntr;
vector <int> h, g[maxn];
bool ok[maxn], isr[maxn];

namespace mathematics{
  ll fac[maxn], fin[maxn];

  ll qpow(ll x, ll n){
	ll y = 1;
	for(; n; n >>= 1, x = x * x % mod)
	  if(n & 1) y = y * x % mod;
	return y;
  }

#define inv(x) qpow(x, mod - 2)

  ll C(ll n, ll m){ return fac[n] * fin[m] % mod * fin[n - m] % mod; }

  void init(int n){
	fac[0] = 1; rep(i, 1, n) fac[i] = fac[i - 1] * i % mod;
	fin[n] = inv(fac[n]);
	per(i, n, 1) fin[i - 1] = fin[i] * i % mod;
  }

  void add(ll &a, ll b){ a = (a + b) % mod; }

  void mul(ll &a, ll b){ a = (a * b) % mod; }
}
using namespace mathematics;

struct edge{ int v, nxt; }; vector <edge> e; int head[maxn];

void adde(int u, int v){
  e.push_back((edge){v, head[u]});
  head[u] = e.size() - 1;
}

void rebuild(int u, int f){
  mrk[u] = tim;
  for(int i = head[u]; ~i; i = e[i].nxt){
	int v = e[i].v; if(v == f) continue;
	if(mrk[v] == tim && v < u) continue;
	if(to[u] == v){
	  if(to[v] > u) g[v].push_back(u), ok[u] = true;
	}
	else{
	  if(to[u] > v) g[u].push_back(v), ok[v] = true;
	}
	if(mrk[v] != tim) rebuild(v, u);
  }
}

void getsize(int u, int f){
  siz[u] = 1;
  for(int i = 0; i < g[u].size(); ++ i){
	int v = g[u][i];
	getsize(v, u);
	siz[u] += siz[v];
  }
}

ll getdp(int u, int f){
  int tot = siz[u] - 1;
  ll res = 1;
  for(int i = 0; i < g[u].size(); ++ i){
	int v = g[u][i];
	mul(res, C(tot, siz[v]) * getdp(v, u) % mod);
	tot -= siz[v];
  }
  return res;
}

ll query(){
  ++ tim; 
  rep(i, 1, cntp) ok[p[i]] = false;
  rebuild(p[1], 0);
  /*
  rep(i, 1, cntp) if(g[p[i]].size()){
	for(int j = 0; j < g[p[i]].size(); ++ j)
	  printf("%d -> %d\n", p[i], g[p[i]][j]);
  }
  */
  int tot = cntp;
  ll res = 1;
  rep(i, 1, cntp) if(!ok[p[i]]){
	getsize(p[i], 0);
	mul(res, C(tot, siz[p[i]]) * getdp(p[i], 0) % mod);
	tot -= siz[p[i]];
  }
  rep(i, 1, cntp) g[p[i]].clear();
  return res;
}

bool getroll(int u, int f){
  h.push_back(u);
  mrk[u] = 2;
  for(int i = head[u]; ~i; i = e[i].nxt){
	int v = e[i].v;
	if(v == f) continue;
	if(mrk[v] != 2){
	  if(getroll(v, u)) return true;
	}
	else if(mrk[v] == 2){
	  int t = -1;
	  while(t != v){
		t = h.back();
		h.pop_back();
		r[cntr ++] = t;
		isr[t] = 1;
	  }
	  return true;
	}
  }
  h.pop_back();
  return false;
}

void match(int u, int f){
  for(int i = head[u]; ~i; i = e[i].nxt){
	int v = e[i].v; if(v == f || isr[v]) continue;
	to[v] = u; match(v, u);
  }
}

ll cal(){
  if(cntp != sdu){
	puts("0");
	exit(0);
  }
  cntr = 0;
  h.clear();
  getroll(p[1], 0);
  /*
  rep(i, 0, cntr - 1) printf("%d ", r[i]); puts("");
  */
  rep(i, 0, cntr - 1) match(r[i], 0);
  rep(i, 0, cntr - 1) to[r[i]] = r[(i + 1) % cntr];
  ll res = query();
  rep(i, 0, cntr - 1) to[r[i]] = r[(i + cntr - 1) % cntr];
  add(res, query());
  return res;
}

void dfs(int u){
  p[++ cntp] = u;
  mrk[u] = 1;
  for(int i = head[u]; ~i; i = e[i].nxt){
	int v = e[i].v;
	sdu ++;
	if(mrk[v]) continue;
	dfs(v);
  }
}

void sol(){
  int tot = 2 * n;
  ll res = 1;
  rep(i, 1, 2 * n) if(!mrk[i]){
	cntp = sdu = 0;
	dfs(i);
	sdu /= 2;
	mul(res, C(tot, cntp) * cal() % mod);
	tot -= cntp;
  }
  printf("%lld\n", res);
}

int main(){
  memset(head, -1, sizeof(head));
  rd(n);
  init(2 * n);
  rep(i, 1, 2 * n){
	int u, v; rd(u), rd(v);
	adde(u, v + n);
	adde(v + n, u);
  }
  sol();
  return 0;
}
/*
7
3 5
7 3
6 4
7 2
2 7
3 6
5 7
4 6
5 2
6 7
1 6
1 1
6 3
4 1
*/


Measured memory_bytes: 23532