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

const int64_t MOD = 1e9+7;
void add(int64_t& a, int64_t b){
    a = (a+b) % MOD;
}
void mul(int64_t& a, int64_t b){
    a = a*b % MOD;
}

vector<int64_t> fact, seq_inv, fact_inv;

void create_fact_mod(int num){
    fact[0] = 1;
    fact[1] = 1;
    for(int i=2; i<=num; i++){
        fact[i] = fact[i-1] * i % MOD;
    }
}

void create_seq_inv_mod(int num){
    seq_inv[0] = 1;
    seq_inv[1] = 1;
    for(int i=2; i<=num; i++){
        seq_inv[i] = (MOD - MOD/i) * seq_inv[MOD%i] % MOD;
    }
}

void create_fact_inv_mod(int num){
    fact_inv[0] = 1;
    fact_inv[1] = 1;
    for(int i=2; i<=num; i++){
        fact_inv[i] = fact_inv[i-1] * seq_inv[i] % MOD;
    }
}

void create_mod_tables(int num){
    fact.resize(num+1);
    seq_inv.resize(num+1);
    fact_inv.resize(num+1);
    create_fact_mod(num);
    create_seq_inv_mod(num);
    create_fact_inv_mod(num);
}

int64_t comb_mod(int n, int k){
    return fact[n] * fact_inv[n-k] % MOD * fact_inv[k] % MOD;
}

int64_t perm_mod(int n, int k){
    return fact[n] * fact_inv[n-k] % MOD;
}

int64_t power_mod(int64_t num, int64_t power){
    int64_t prod = 1;
    num %= MOD;
    while(power > 0){
        if(power&1) prod = prod * num % MOD;
        num = num * num % MOD;
        power >>= 1;
    }
    return prod;
}

int64_t extgcd(int64_t a, int64_t b, int64_t& x, int64_t& y){
    int64_t d = a;
    if(b != 0){
        d = extgcd(b, a%b, y, x);
        y -= (a/b) * x;
    }else{
        x = 1; y = 0;
    }
    return d;
}

int64_t inv_mod(int64_t a){
    int64_t x, y;
    extgcd(a, MOD, x, y);
    return (MOD + x%MOD) % MOD;
}

struct UnionFind {
    vector<int> par;
    vector<int> sz, es;
    vector<vector<int>> items;

    UnionFind(int n=0){
        if(n>0) initialize(n);
    }

    void initialize(int n){
        par.resize(n);
        sz.resize(n);
        es.resize(n);
        items.resize(n);
        for(int i=0; i<n; i++){
            par[i] = i;
            sz[i] = 1;
            items[i] = {i};
        }
    }

    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);
        es[x]++;
        if(x == y) return;
        if(sz[x] > sz[y]) swap(x, y);
        par[x] = y;
        sz[y] += sz[x];
        es[y] += es[x];
        for(int a : items[x]) items[y].push_back(a);
    }

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

    int size(int x){
        return sz[find(x)];
    }
    int esize(int x){
        return es[find(x)];
    }
};

void fail(){
    cout << 0 << endl;
    exit(0);
}

int main(){
    int N;
    cin >> N;
    create_mod_tables(2*N);
    UnionFind uf(2*N);
    vector<set<int>> edges(2*N);
    vector<vector<int>> edges_org(2*N);
    for(int i=0; i<2*N; i++){
        int x, y;
        cin >> x >> y;
        x--;
        y += N-1;
        edges[x].insert(y);
        edges[y].insert(x);
        edges_org[x].push_back(y);
        edges_org[y].push_back(x);
        uf.unite(x, y);
    }
    for(int i=0; i<2*N; i++) if(uf.size(i) != uf.esize(i)) fail();

    vector<int> ball(2*N, -1);
    int64_t C = fact[2*N], ans = 1;
    for(int pa=0; pa<2*N; pa++) if(uf.find(pa) == pa){
        auto vs = uf.items[pa];
        int sz = vs.size();
        mul(C, fact_inv[sz]);
        
        queue<int> que;
        for(int a : vs) if(edges[a].size() == 1) que.push(a);
        while(que.size()){
            int a = que.front(); que.pop();
            int b = *edges[a].begin();
            ball[a] = b;
            edges[a].erase(b);
            edges[b].erase(a);
            if(edges[b].size() == 1) que.push(b);
        }

        vector<int> cycle;
        for(int a : vs) if(edges[a].size()){
            int prv = a, now = *edges[a].begin();
            cycle = {a};
            while(now != a){
                cycle.push_back(now);
                int nxt = *edges[now].begin();
                if(nxt == prv) nxt = *edges[now].rbegin();
                prv = now; now = nxt;
            }
            break;
        }

        int64_t res = 0;
        for(int t=0; t<2; t++){
            int csz = cycle.size();
            for(int i=0; i<csz; i++) ball[cycle[i]] = cycle[(i+1)%csz];

            static vector<int> to(2*N+1), indeg(2*N+1), psz(2*N+1);
            static vector<int64_t> dp(2*N+1);
            for(int a : vs){
                to[a] = -1; indeg[a] = 0; psz[a] = 0; dp[a] = 1;
            }
            indeg[2*N] = 1e9; psz[2*N] = 0; dp[2*N] = 1;
            for(int a : vs) for(int b : edges_org[a]) if(b < ball[a] && ball[b] == a){
                to[b] = a;
                indeg[a]++;
            }
            for(int a : vs) if(to[a] == -1) to[a] = 2*N;
            queue<int> que;
            for(int a : vs) if(indeg[a] == 0) que.push(a);
            while(que.size()){
                int a = que.front(); que.pop();
                int b = to[a];
                psz[a]++;
                indeg[b]--;
                if(indeg[b] == 0) que.push(b);
                mul(dp[b], dp[a]);
                mul(dp[b], comb_mod(psz[b]+psz[a], psz[a]));
                psz[b] += psz[a];
            }
            add(res, dp[2*N]);

            reverse(cycle.begin(), cycle.end());
        }
        mul(ans, res);
    }
    mul(ans, C);
    cout << ans << endl;
    return 0;
}


Measured memory_bytes: 68460