#include <bits/stdc++.h>
using namespace std;
typedef long long lint;
using pi = pair<int, int>;
const int MAXN = 200005;
const int mod = 1e9 + 7;

struct disj{
	int pa[MAXN];
	void init(int n){
		iota(pa, pa + n + 1, 0);
	}
	int find(int x){
		return pa[x] = (pa[x] == x ? x : find(pa[x]));
	}
	bool uni(int p, int q){
		p = find(p);
		q = find(q);
		if(p == q) return 0;
		pa[q] = p; return 1;
	}
}disj;

int n, sz[MAXN], indeg[MAXN];
vector<pi> gph[MAXN];
vector<int> tr[MAXN];
vector<int> dfn;
int deg[MAXN], vis[MAXN];
int match[MAXN];
lint fact[MAXN], invf[MAXN];

lint dfs2(int x){
	sz[x] = 0;
	lint ret = 1;
	for(auto &i : tr[x]){
		ret *= dfs2(i);
		ret %= mod;
	}
	for(auto &i : tr[x]){
		sz[x] += sz[i];
		ret *= invf[sz[i]];
		ret %= mod;
	}
	ret *= fact[sz[x]];
	ret %= mod;
	sz[x]++;
	return ret;
}

lint topsort(){
	vector<pi> edges; // fi -> se
	vector<int> edge_list;
	for(auto &i : dfn){
		int to_match = -1;
		for(auto &j : gph[i]){
			if(match[i] == j.second){
				to_match = j.first;
			}
		}
		assert(to_match != -1);
		edge_list.push_back(to_match);
		for(auto &j : gph[i]){
			if(match[i] > j.second){
				edges.emplace_back(j.first, to_match);
			}
		}
	}
	for(auto &i : edge_list) disj.pa[i] = i;
	for(auto &i : edges){
		if(!disj.uni(i.first, i.second)){
			return 0;
		}
	}
	for(auto &i : edges){
		tr[i.second].push_back(i.first);
		indeg[i.first]++;
	}
	lint ret = 1;
	for(auto &i : edge_list){
		if(indeg[i] == 0){
			ret *= dfs2(i) * invf[sz[i]] % mod;
			ret %= mod;
		}
	}
	ret *= fact[edge_list.size()];
	for(auto &i : edges){
		indeg[i.first] = 0;
		tr[i.second].clear();
	}
	return ret % mod;
}

lint solve(){
	queue<int> que;
	for(auto &i : dfn){
		if(deg[i] == 1){
			que.push(i);
		}
	}
	while(!que.empty()){
		int x = que.front(); que.pop();
		deg[x]--;
		for(auto &i : gph[x]){
			if(deg[i.second]){
				match[x] = i.second;
				deg[i.second]--;
				if(deg[i.second] == 1){
					que.push(i.second);
				}
			}
		}
	}
	vector<int> cyc_ord;
	for(auto &i : dfn){
		if(deg[i] == 2){
			for(int j=i; deg[j]; ){
				deg[j] = 0;
				cyc_ord.push_back(j);
				for(auto &k : gph[j]){
					if(deg[k.second]){
						j = k.second;
						break;
					}
				}
			}
		}
	}
	int ret = 0;
	for(int i=0; i<cyc_ord.size(); i++){
		match[cyc_ord[i]] = cyc_ord[(i+1)%cyc_ord.size()];
	}
	ret += topsort();
	reverse(cyc_ord.begin(), cyc_ord.end());
	for(int i=0; i<cyc_ord.size(); i++){
		match[cyc_ord[i]] = cyc_ord[(i+1)%cyc_ord.size()];
	}
	ret += topsort();
	return ret % mod;
}

int v, e;

void dfs(int x){
	if(vis[x]) return;
	dfn.push_back(x);
	vis[x] = 1;
	v++;
	e += gph[x].size();
	for(auto &i : gph[x]) dfs(i.second);
}

lint ipow(lint x, lint p){
	lint ret = 1, piv = x;
	while(p){
		if(p & 1) ret = ret * piv % mod;
		piv = piv * piv % mod;
		p >>= 1;
	}
	return ret;
}
int main(){
	fact[0] = invf[0] = 1;
	for(int i=1; i<MAXN; i++){
		fact[i] = fact[i-1] * i % mod;
		invf[i] = ipow(fact[i], mod - 2);
	}
	scanf("%d",&n);
	for(int i=0; i<2*n; i++){
		int x, y; scanf("%d %d",&x,&y);
		x--, y += n - 1;
		gph[x].emplace_back(i, y);
		gph[y].emplace_back(i, x);
		deg[x]++;
		deg[y]++;
	}
	n <<= 1;
	disj.init(n);
	lint ret = 1;
	for(int i=0; i<n; i++){
		if(!vis[i]){
			v = e = 0;
			dfn.clear();
			dfs(i);
			if(2 * v != e){
				puts("0");
				return 0;
			}
			ret *= invf[v] * solve() % mod;
			ret %= mod;
		}
	}
	ret *= fact[n];
	cout << ret % mod << endl;
}


Measured memory_bytes: 33512