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

const int N = 1e6 + 5;

#define st first
#define nd second

typedef pair<int,int> pun;
typedef long long ll;

int mod = 1000 * 1000 * 1000 + 7;

long long pow(int a, int k) {
	if (k == 0) return 1;
	if (k == 1) return a;
	long long b = pow(a, k / 2);
	b = b * b % mod;
	if (k % 2) b = a * b % mod;
	return b;
}

long long odw(int a) {
	return pow(a, mod - 2);
}

long long silnia[N];
long long odw_silnia[N];

long long newton(int n, int k) {
	return (silnia[n] * odw_silnia[n - k] % mod) * odw_silnia[k] % mod;
}

vector<int> G[N];
int deg[N];
int skierowany[N];
vector<int> odw_skierowany[N];
int vis[N];
int timer = 0;

long long wynik = 1;
int size_so_far;
vector<int> skladowa;

void dfs_skladowa(int x) {
	if (vis[x]) return;
	vis[x] = timer;
	skladowa.push_back(x);
	for (int y : G[x]) {
		dfs_skladowa(y);
	}
}

vector<int> cykl;

void dfs_cykl(int x) {
	if (vis[x] == timer) return;
	vis[x] = timer;
	cykl.push_back(x);
	for (int y: G[x]) dfs_cykl(y);
}

long long mozliwosci;
long long tmp;

vector<int> F[N];
vector<int> transF[N];

int size[N];
long long res[N];

void dfs_dziwny(int x) {
	if (vis[x] == timer) return;
	vis[x] = timer;
	size[x] = 0;
	res[x] = 1;
	for (int y: F[x]) {
		dfs_dziwny(y);
		size[x] += size[y];
		res[x] = (res[x] * res[y]) % mod;
		res[x] = (res[x] * newton(size[x], size[y])) % mod;
	}
	size[x] ++;
// 	printf("dfs dziwny %d ", x);
// 	printf("%lld\n", res[x]);
}

void oblicz(int root, int size) {
// 	printf("oblicz %d\n", root);
	timer ++;
	for (int x: skladowa) {
		F[x].clear();
		transF[x].clear();
	}
	F[0].clear();
	for (int x: skladowa) {
		for (int y: odw_skierowany[x]) {
			if (y < skierowany[x]) {
				F[x].push_back(y);
				transF[y].push_back(x);
			}
		}
	}
	for (int x: skladowa) {
		if (transF[x].empty() && (x != root || size == skladowa.size())) {
			F[0].push_back(x);
		}
	}
	dfs_dziwny(0);
	mozliwosci += res[0];
	mozliwosci %= mod;
}


void algos(int x) {
	skladowa.clear();
	timer ++;
	dfs_skladowa(x);
	timer ++;
	queue<int> kol;
	int stopnie = 0;
	for (int y : skladowa) {
		stopnie += deg[y];
		if (deg[y] <= 1) {
			kol.push(y);
		}
	}
	if (stopnie > skladowa.size() * 2) wynik = 0;
	int root = x;
	while(!kol.empty()) {
		int y = kol.front();
		root = y;
		vis[y] = timer;
		kol.pop();
		for (int z : G[y]) {
			if (vis[z] != timer) {
				skierowany[y] = z;
				odw_skierowany[z].push_back(y);
				deg[z] --;
				if (deg[z] == 1) {
					kol.push(z);
				}
			}
		}
	}
	mozliwosci = 0;
	for (int y: skladowa) {
		if (vis[y] != timer) {
			//Medusa
			cykl.clear();
			dfs_cykl(y);
			for (int i = 0; i < cykl.size() - 1; i ++) {
				skierowany[cykl[i]] = cykl[i+1];
				odw_skierowany[cykl[i+1]].push_back(cykl[i]);
			}
			skierowany[cykl.back()] = cykl[0];
			odw_skierowany[cykl[0]].push_back(cykl.back());
			oblicz(cykl.back(), skladowa.size());
			for (int i = 0; i < cykl.size(); i ++) {
				odw_skierowany[cykl[i]].pop_back();
			}
			for (int i = cykl.size() - 1; i > 0; i --) {
				skierowany[cykl[i]] = cykl[i-1];
				odw_skierowany[cykl[i-1]].push_back(cykl[i]);
			}
			skierowany[cykl[0]] = cykl.back();
			odw_skierowany[cykl.back()].push_back(cykl[0]);
			oblicz(cykl[0], skladowa.size());
			goto end;
		}
	}
	//Tree
	oblicz(root, skladowa.size() - 1);
	
	end:
	size_so_far += skladowa.size();
	wynik *= newton(size_so_far, skladowa.size());
	wynik %= mod;
	wynik *= mozliwosci;
	wynik %= mod;
}

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 0; i < 2 * n; i ++ ){
		int a, b;
		scanf("%d%d", &a, &b);
		G[a].push_back(b + n);
		G[b + n].push_back(a);
		deg[a] ++;
		deg[b + n] ++;
	}
	silnia[0] = 1;
	odw_silnia[0] = 1;
	for (int i = 1; i <= 2 * n; i ++) {
		silnia[i] = silnia[i - 1] * i % mod;
		odw_silnia[i] = odw_silnia[i - 1] * odw(i) % mod;
	}
	for (int i = 1; i <= 2 * n; i ++) {
		if (vis[i] == 0) {
			algos(i);
		}
	}
	printf("%lld\n", wynik);
}


Measured memory_bytes: 135544