#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#define MAXN 200005
#define mod 1000000007
using namespace std;

int n, mark[MAXN], deg[MAXN], dp[MAXN], sz[MAXN];
int fact[MAXN], ifact[MAXN];
vector<int> G[MAXN], Gmark[MAXN];
vector<pair<int, int>> partialSol;
bool uz[MAXN], uz2[MAXN], inCycle[MAXN];

int pwmod(int x, int n) {
    if (n == 0)
        return 1;
    return 1LL * pwmod(1LL * x * x % mod, n >> 1) * ((n & 1) ? x : 1) % mod;
}

void precalcFact() {
    fact[0] = ifact[0] = 1;
    for (int i = 1; i < MAXN; ++i) {
        fact[i] = 1LL * fact[i - 1] * i % mod;
        ifact[i] = pwmod(fact[i], mod - 2);
    }
}

int comb(int n, int k) {
    return 1LL * fact[n] * ifact[k] % mod * ifact[n - k] % mod;
}

void DFS(int u, int &numNodes, int &numEdges) {
    uz[u] = 1;
    ++numNodes;

    for (auto x: G[u]) {
        ++numEdges;
        if (!uz[x])
            DFS(x, numNodes, numEdges);
    }
}

void findCycle(int u, int p, vector<int> &cycle, vector<int> &st, vector<int> &nodes) {
    st.push_back(u);
    nodes.push_back(u);
    uz2[u] = 1;

    for (auto x: G[u]) {
        if (x == p)
            continue;
        if (uz2[x]) {
            if (cycle.empty()) {
                for (int j = (int) st.size() - 1; j >= 0 && st[j] != x; j--)
                    cycle.push_back(st[j]);
                cycle.push_back(x);
            }
        }
        else
            findCycle(x, u, cycle, st, nodes);
    }

    st.pop_back();
}

void DFSmark(int u, int p) {
    for (auto x: G[u]) {
        if (x != p && !inCycle[x]) {
            mark[x] = u;
            DFSmark(x, u);
        }
    }
}

bool getTopSort(vector<int> &nodes, vector<int> &topSort) {
    queue<int> q;

    q.push(0);
    while (!q.empty()) {
        int x = q.front();
        q.pop();

        topSort.push_back(x);
        for (auto y: Gmark[x]) {
            if (--deg[y] == 0)
                q.push(y);
        }
    }

    return topSort.size() == nodes.size();
}

int solveMark(vector<int> &nodes) {
    nodes.push_back(0);
    for (auto x: nodes) {
        Gmark[x].clear();
        deg[x] = dp[x] = sz[x] = 0;
    }

    for (auto x: nodes) {
        for (auto y: G[x]) {
            if (y == mark[x])
                break;
            Gmark[x].push_back(y);
            ++deg[y];
        }
    }
    for (auto x: nodes) {
        if (x != 0 && deg[x] == 0) {
            Gmark[0].push_back(x);
            ++deg[x];
        }
    }

    vector<int> topSort;
    if (!getTopSort(nodes, topSort)) {
        nodes.pop_back();
        return -1;
    }

    reverse(topSort.begin(), topSort.end());
    for (auto x: topSort) {
        long long dpx = 1;
        int crtSz = 0;

        for (auto y: Gmark[x]) {
            crtSz += sz[y];
            dpx = dpx * dp[y] % mod * comb(crtSz, sz[y]) % mod;
        }
        sz[x] = crtSz + 1;
        dp[x] = (int) dpx;
    }

    nodes.pop_back();
    return dp[0];
}

pair<int, int> solve(int u) {
    vector<int> cycle, st, nodes;

    findCycle(u, 0, cycle, st, nodes);

    for (auto x: cycle)
        inCycle[x] = 1;

    for (auto x: cycle)
        DFSmark(x, 0);

    cycle.push_back(cycle[0]);
    int cycleSize = (int) cycle.size();
    for (int i = 0; i < cycleSize - 1; ++i)
        mark[cycle[i]] = cycle[i + 1];

    int sol1 = solveMark(nodes);

    for (int i = 1; i < cycleSize; ++i)
        mark[cycle[i]] = cycle[i - 1];

    int sol2 = solveMark(nodes);

    if (sol1 < 0 && sol2 < 0)
        return make_pair(-1, -1);
    sol1 = max(sol1, 0);
    sol2 = max(sol2, 0);

    return make_pair((sol1 + sol2) % mod, (int) nodes.size());
}

int main() {
    ios::sync_with_stdio(0);

    precalcFact();
    cin >> n;
    for (int i = 1; i <= 2 * n; ++i) {
        int x, y;
        cin >> x >> y;
        G[x].push_back(y + n);
        G[y + n].push_back(x);
    }

    for (int i = 1; i <= 2 * n; ++i)
        sort(G[i].begin(), G[i].end());

    for (int i = 1; i <= 2 * n; ++i) {
        if (!uz[i]) {
            int numNodes = 0;
            int numEdges = 0;
            
            DFS(i, numNodes, numEdges);
            numEdges >>= 1;

            if (numEdges != numNodes) {
                cout << "0\n";
                return 0;
            }
            pair<int, int> crtSol = solve(i);
            if (crtSol.first < 0) {
                cout << "0\n";
                return 0;
            }

            partialSol.push_back(crtSol);
        }
    }

    long long ans = 1;
    int crtSz = 0;

    for (auto x: partialSol) {
        crtSz += x.second;
        ans = ans * x.first % mod * comb(crtSz, x.second) % mod;
    }

    cout << ans << "\n";

    return 0;
}

Measured memory_bytes: 33852