#include <bits/stdc++.h>

using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
template <typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
template <typename T> T sgn(const T&a) { return (a > T(0)) - (a < T(0)); }
int read() {
	int x = 0, flag = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) flag *= (ch == '-' ? -1 : 1);
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	return flag * x;
}
void write(LL x) {
	if (x < 0) putchar('-'), x = -x;
	if (x >= 10) write(x / 10);
	putchar(x % 10 + '0');
}

const int Maxn = 5e5 + 9, Mod = 1e9 + 7;
namespace Math {
	int fpm(int base, int tims) {
		int r = 1;
		for (; tims; base = 1ll * base * base % Mod, tims >>= 1) 
			if (tims & 1) r = 1ll * r * base % Mod;
		return r;
	}
	int fac[Maxn], invFac[Maxn];
	void Init() {
		fac[0] = 1;
		rep (i, 1, Maxn - 1) 
			fac[i] = fac[i - 1] * 1ll * i % Mod;
		invFac[Maxn - 1] = fpm(fac[Maxn - 1], Mod - 2);
		drep (i, Maxn - 2, 0) invFac[i] = invFac[i + 1] * (i + 1ll) % Mod;
	}
	int C(int _n, int _m) {
		if (_n < _m) return 0;
		return fac[_n] * 1ll * invFac[_n - _m] % Mod * invFac[_m] % Mod;
	}
}
using namespace Math;

struct node { int u, v, w; };
struct edge { int to, nxt, w; }g[Maxn << 2], g1[Maxn << 2];
int head[Maxn], e;
int head1[Maxn], e1;
void add(int u, int v, int w) { g[++e] = (edge){v, head[u], w}, head[u] = e; }
void add1(int u, int v, int w = 0) { g1[++e1] = (edge){v, head1[u], w}, head1[u] = e1; }

int vis[Maxn], inCir[Maxn], found, _top, delta, n;
node stk[Maxn];
vector <node> cir;
vector <int> cus, cun;

void dfsCir(int u, int pa) {
	if (found) return ;
	vis[u] = 1; 
		
	for (int i = head[u]; ~i; i = g[i].nxt) {
		int v = g[i].to;
		if (v != pa) {
			if (!vis[v]) {
				stk[++_top] = (node){u, v, g[i].w};
				dfsCir(v, u);
				--_top;
			} else if (!found) {
				node tmp; int bak = _top;
				do {
					tmp = stk[bak--];
					cir.push_back(tmp);
					if (!inCir[tmp.u]) cus.push_back(tmp.u);
					if (!inCir[tmp.v]) cus.push_back(tmp.v);
					inCir[tmp.u] = inCir[tmp.v] = 1;
				} while (tmp.u != v);
				cir.push_back((node){u, v, g[i].w});
				found = 1;
				return;
			} 
		}
		if (found) return;
	}
}

int val[Maxn], fa[Maxn];
int find(int x) {
	return fa[x] ^ x ? fa[x] = find(fa[x]) : x;
}
void dfs1(int u, int pa) {
	if (delta > n) return;
	cun.push_back(u); ++delta; 
	for (int i = head[u]; ~i; i = g[i].nxt) {
		int v = g[i].to;
		if (!inCir[v] && v != pa) val[v] = g[i].w, dfs1(v, u);
	}
}

int size[Maxn], dp[Maxn];
void dfsDp(int u, int pa) {
	dp[u] = 1;
	int sum = 0;
	for (int i = head1[u]; ~i; i = g1[i].nxt) {
		int v = g1[i].to;
		if (v != pa) {
			dfsDp(v, u);
			size[u] += size[v];
			dp[u] = 1ll * dp[u] * dp[v] % Mod * C(size[u], sum) % Mod;
			sum += size[v];
		}
	}
	++size[u];
}
int deg[Maxn];
int calc() {
	e1 = 0;
	rep (i, 0, cun.size() - 1) {
		int u = cun[i];
		size[u] = dp[u] = deg[u] = 0;
		head1[u] = -1;
	}
	rep (ct, 0, cun.size() - 1) {
		int u = cun[ct];
		for (int i = head[u]; ~i; i = g[i].nxt) {
			int v = g[i].to;
			if (g[i].w < val[u]) {
				add1(u, v), ++deg[v];
				//cout << u << " " << v << endl;
			}
		}
	}
	int res = 1, sum = 0;
	rep (ct, 0, cun.size() - 1) {
		int u = cun[ct];
		if (!deg[u]) {
			dfsDp(u, 0);
			res = 1ll * res * dp[u] % Mod * C(sum + size[u], sum) % Mod;
			sum += size[u];
		}
	}
	//cout << res << endl;
//	puts("");
	return res;
}

int main() {

	static int x[Maxn], y[Maxn], xjig[Maxn], amt = 0;

	clar (head, -1), e = 0;
	Math :: Init();

	n = read() << 1;
	rep (i, 1, n) fa[i] = i;
	rep (i, 1, n) {
		x[i] = read(), y[i] = read();
		add(x[i], y[i] + n / 2, x[i] + y[i]);
		add(y[i] + n / 2, x[i], x[i] + y[i]);
		if (!xjig[x[i]]) ++amt, xjig[x[i]] = 1;
		if (!xjig[y[i] + n / 2]) ++amt, xjig[y[i] + n / 2] = 1;
		if (find(x[i]) != find(y[i] + n / 2)) fa[find(x[i])] = fa[find(y[i] + n / 2)];
//		cout << x[i] << " " << y[i] + n / 2 << endl;
	}
	if (amt != n) {
		puts("0");
		return 0;
	}
	LL ans = 1, sum = 0;
	rep (i, 1, n) 
		if (fa[i] == i) {
			int res = 0; _top = 0, cir.clear(), found = 0, delta = 0, cus.clear(), cun.clear();
			dfsCir(i, 0);
			if (!found) {
				ans = 0;
				break;
			}
			rep (j, 0, cus.size() - 1) dfs1(cus[j], 0);
			if (delta > n) {
				ans = 0;
				break;
			}
			rep (j, 0, cir.size() - 1) val[cir[j].u] = cir[j].w;
			res += calc();
			if (res >= Mod) res -= Mod;
				rep (j, 0, cir.size() - 1) val[cir[j].v] = cir[j].w;
			res += calc();
			if (res >= Mod) res -= Mod;

			ans = 1ll * ans * res % Mod * C(sum += delta, delta) % Mod;
		}
	printf("%d\n", 1ll * ans * fpm(fac[n], Mod - 1) % Mod);

#ifdef Qrsikno
    debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return 0;
}


Measured memory_bytes: 46460