#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
const int N = 5 * 100000 + 100;
const int MOD = 1000000007;
const int oo = 1e9;

int n , x , y, take[N], in[N], sz[N];
bool inStack[N], vis[N];
ll fact[N], inv[N];

vector<int> g[N], tree[N], cycle, my;
stack<int> st;

void findCycle(int u , int p) {
	if (inStack[u]) {
		while (1) {
			cycle.pb(st.top());
			st.pop();
			if (cycle.back() == u)break;
		}
		for (int i = 0; i < cycle.size(); ++i) st.push(cycle[i]);
		return;
	}
	if (vis[u])return;
	my.pb(u);
	take[u] = p;
	vis[u] = 1;
	inStack[u] = 1;
	st.push(u);
	for (int i = 0; i < g[u].size(); ++i) {
		int v = g[u][i];
		if (v == p)continue;
		findCycle(v, u);
	}
	inStack[u] = 0;
	st.pop();
}
bool getType(int idx) {
	return idx > n;
}
ll fast(ll a , ll b) {
	if (!b)return 1ll;
	ll res = fast(a, b / 2);
	res = (res * res) % MOD;
	if (b & 1)
		return (res * a) % MOD;
	return res;
}
ll nCk(ll n , ll k) {
	return ((fact[n] * inv[n - k]) % MOD * inv[k]) % MOD;
}
ll calc(int u) {
	sz[u] = 0;
	ll ans = 1;
	for (int i = 0; i < tree[u].size(); ++i) {
		int v = tree[u][i];
		ll ff = calc(v);
		ans = (((ans * ff ) % MOD * nCk(sz[u] + sz[v], sz[v]))) % MOD;
		sz[u] += sz[v];
	}
	sz[u]++;
	return ans;
}
int dsu[N], ed[N], fn[N];

int find(int u) {
	return dsu[u] == u ? u : dsu[u] = find(dsu[u]);
}
int main() {
	scanf("%d", &n);
	for (int i = 1 ; i <= 2 * n; ++i) {
		dsu[i] = i;
		fn[i] = 1;
	}
	for (int i = 0; i < 2 * n; ++i) {
		scanf("%d%d", &x, &y);
		g[x + n].pb(y);
		g[y].pb(x + n);
		if (find(x + n) != find(y)) {
			fn[find(y)] += fn[find(x + n)];
			ed[find(y)] += ed[find(x + n)];
			dsu[find(x + n)] = find(y);
		}
		ed[find(x + n)]++;
	}
	for (int i = 1; i <= 2 * n; ++i) {
		if (find(i) == i && fn[i] !=  ed[i]) {
			puts("0");
			return 0;
		}
	}

	fact[0] = 1;
	inv[0] = fast(fact[0], MOD - 2);
	for (int i = 1 ; i <= 2 * n ; ++i) {
		fact[i] = (fact[i - 1] * i) % MOD;
		inv[i] = fast(fact[i], MOD - 2);
	}
	ll ans = 1;
	int len = 0;
	for (int node = 1 ; node <= 2 * n ; ++node) {
		if (vis[node])continue;
		cycle.clear();
		my.clear();
		findCycle(node, -1);
		if (cycle.size()) {
			for (int i = 0; i < my.size(); ++i) {
				inStack[my[i]] = vis[my[i]] = 0;
			}
			int root = cycle[0];
			cycle.clear();
			my.clear();
			findCycle(root, -1);
		}
		ll res = 0;
		for (int it = 0; it < 2 ; ++it) {
			for (int i = 0 ; i < my.size(); ++i) {
				in[my[i]] = 0;
				tree[my[i]].clear();
			}
			tree[0].clear();
			for (int j = 0 ; j < cycle.size(); ++j) {
				take[cycle[j]] = (it == 0 ? cycle[(j + 1) % cycle.size()] :  cycle[(j - 1 + cycle.size()) % cycle.size()]);
			}
			for (int i = 0; i < my.size() ; ++i) {
				int f = my[i];
				for (int j = 0 ; j < g[f].size(); ++j) {
					int v = g[f][j];
					if (getType(take[f]) == getType(v) && v < take[f]) {
						tree[f].pb(v);
						in[v]++;
					}
				}
			}
			for (int i = 0; i < my.size(); ++i) {
				if (!in[my[i]]) {
					tree[0].push_back(my[i]);
				}
			}
			res = (res + calc(0)) % MOD;
		}

		ans = (ans * res ) % MOD;
		ans = (ans * nCk(len + my.size(), my.size())) % MOD;
		len += my.size();
	}
	printf("%lld\n", ans );
	return 0;
}


Measured memory_bytes: 52852