#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> pii;
typedef long long ll;
typedef vector<int> vi;

#define pb push_back
#define eb emplace_back
#define mp make_pair
#define fi first
#define se second
#define rep(i,n) rep2(i,0,n)
#define rep2(i,m,n) for(int i=m;i<(n);i++)
#define ALL(c) (c).begin(),(c).end()
#define dump(x) cout << #x << " = " << (x) << endl
constexpr ll TEN(int n) { return (n == 0) ? 1 : 10 * TEN(n-1); }

template<class T, class U>
ostream& operator<<(ostream& os, const pair<T, U>& p) {
	os<<"("<<p.first<<","<<p.second<<")";
	return os;
}

template<class T>
ostream& operator<<(ostream& os, const vector<T>& v) {
	os<<"{";
	rep(i, v.size()) {
		if (i) os<<",";
		os<<v[i];
	}
	os<<"}";
	return os;
}

class unionfind {
	vector<int> par, rank;

public:
	void init(int n) {
		par.resize(n);
		rank.resize(n);

		for (int i = 0; i < n; i++) {
			par[i] = i;
			rank[i] = 0;
		}
	}

	int find(int x) {
		if (par[x] == x) return x;
		else return par[x] = find(par[x]);
	}

	void unite(int x, int y) {
		x = find(x);
		y = find(y);
		if (x == y) return ;

		if (rank[x] < rank[y]) par[x] = y;
		else {
			par[y] = x;
			if (rank[x] == rank[y]) ++rank[x];
		}
	}

	bool same(int x, int y) { return (find(x) == find(y)); }
} uf;

const ll MOD = TEN(9) + 7;
const int MX = 200010;

ll inv[MX], fact[MX], ifact[MX];

void init() {
	inv[1] = 1;
	for (int i = 2; i < MX; ++i) {
		inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
	}
	fact[0] = ifact[0] = 1;
	for (int i = 1; i < MX; ++i) {
		fact[i] = fact[i-1] * i % MOD;
		ifact[i] = ifact[i-1] * inv[i] % MOD;
	}
}

ll comb(int n, int r) {
	if (n < 0 || r < 0 || r > n) return 0;
	return fact[n] * ifact[r] % MOD * ifact[n - r] % MOD;
}

// allow multiple components
const int maxn = 200010;

int V;
int deg[maxn], par[maxn];
vi g[maxn], g2[maxn];
bool vis[maxn];
vector<vi> cycles;

void add_edge(int a, int b) {
	g[a].pb(b); g[b].pb(a);
	++deg[a]; ++deg[b];
}

void dfs(int v, vi& cycle) {
	vis[v] = true;
	cycle.pb(v);

	for (int to : g[v]) if (deg[to] == 2 && !vis[to]) {
		dfs(to, cycle);
	}
}

void build() {
	memset(par, -1, sizeof(par));

	queue<int> que;
	rep(i, V) {
		if (deg[i] == 1) {
			que.push(i);
		}
	}

	while (!que.empty()) {
		int v = que.front(); que.pop();
		vis[v] = true;
		for (int to : g[v]) if (deg[to] > 1) {
			g2[to].pb(v);
			par[v] = to;

			if (--deg[to] == 1) {
				que.push(to);
			}
		}
	}

	rep(i, V) if (deg[i] == 2 && !vis[i]) {
		vi cyc;
		cycles.eb(cyc);
		dfs(i, cycles.back());
	}
}

int dp[maxn];

bool us[maxn];
int ord[maxn];

void topo(int v, int &k, const vector<vi>& gp) {
	us[v] = true;
	for (int to : gp[v]) {
		if (!us[to]) {
			topo(to, k, gp);
		}
	}
	ord[v] = k++;
}

int reach(int v, const vector<vi>& gp) {
	if (dp[v] != -1) {
		return dp[v];
	}
	int s = 0;
	for (int to : gp[v]) {
		s += reach(to, gp) + 1;
	}
	return dp[v] = s;
}

vi cmp[maxn];
int id[maxn];

int main() {
	int N; cin >> N; V = N * 2;
	init();
	uf.init(V);

	rep(i, N*2) {
		int x, y; cin >> x >> y;
		--x; --y;
		add_edge(x, y + N);
		uf.unite(x, y + N);
	}

	build();
	bool ok = true;
	rep(i, V) if (!vis[i]) {
		ok = false;
	}

	if (!ok) {
		puts("0");
		return 0;
	}

	ll ans = 1;

	rep(i, V) cmp[uf.find(i)].pb(i);
	int s = 0;

	rep(i, cycles.size()) {
		int c = uf.find(cycles[i][0]);
		vi& vec = cmp[c];
		vi match(vec.size(), -1);
		rep(j, vec.size()) id[vec[j]] = j;

		ll way = 0;
		rep(t, 2) { //try 2 directions
			vector<vi> gp(vec.size());

			rep(j, vec.size()) {
				dp[j] = -1;
				us[j] = 0;
			}

			for (int v : vec) if (par[v] != -1) {
				match[id[v]] = id[par[v]];
			}
			rep(j, cycles[i].size()) {
				int nx = (j + 1) % cycles[i].size();
				match[id[cycles[i][j]]] = id[cycles[i][nx]];
			}
/*
			rep(j, vec.size()) {
				cout << vec[j] << ", id = " << j << " " << match[j] << endl;
			}
*/
			for (int v : vec) {
				int to = match[id[v]];
				for (int u : g[v]) {
					if (u < vec[to]) {
						gp[id[v]].pb(id[u]);
						//cerr << id[v] << "---" << id[u] << endl;
					}
				}
			}

			int now = 0;

			rep(j, vec.size()) {
				if (!us[j]) {
					topo(j, now, gp);
				}
			}

			bool ok = true;
			rep(j, vec.size()) {
				for (int to : gp[j]) {
					if (ord[j] < ord[to]) {
						ok = false;
					}
				}
			}

			ll f = 1;
			if (ok) {
				rep(j, vec.size()) {
					f = f * inv[reach(j, gp) + 1] % MOD;
				}
			} else {
				f = 0;
			}

			way = (way + f) % MOD;

			reverse(ALL(cycles[i]));
		}

		way = way * fact[cmp[c].size()] % MOD;

		ans = ans * way % MOD * comb(s + cmp[c].size(), s) % MOD;
		s += cmp[c].size();
	}

	cout << ans << endl;
	return 0;
}

Measured memory_bytes: 45348