#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>

namespace IO {
    template <typename _T>
    inline bool read (_T& x) {
        x = 0;
        _T y = 1;
        char c = getchar();
        while ((c < '0' || '9' < c) && c != EOF) {
            if (c == '-') y = -1;
            c = getchar();
        }
        if (c == EOF) return false;
        while ('0' <= c && c <= '9') x = x * 10 + c - '0', c = getchar();
        x *= y;
        return true;
    }

    template <typename _T>
    inline _T input () {
        _T x = 0, y = 1;
        char c = getchar();
        while ((c < '0' || '9' < c) && c != EOF) {
            if (c == '-') y = -1;
            c = getchar();
        }
        if (c == EOF) return 0;
        while ('0' <= c && c <= '9') x = x * 10 + c - '0', c = getchar();
        x *= y;
        return x;
    }
};
using namespace IO;

namespace modular {
    const int MOD = 1000000007;

    inline int add (int x, int y) { return (x += y) >= MOD ? x -= MOD : x; }

    inline void inc (int &x, int y) { (x += y) >= MOD ? x -= MOD : 0; }

    inline int mul (int x, int y) { return 1LL * x * y % MOD; }

    inline int qpow (int x, int y) {
        int ans = 1;
        for (; y; y >>= 1, x = mul(x, x)) if (y & 1) ans = mul(ans, x);
        return ans;
    }
};
using namespace modular;

#define reg register
#define MAX_N 200007
#define FIN(s) freopen(s, "r", stdin)
#define FOUT(s) freopen(s, "w", stdout)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define rep(i, l, r) for(int i = l; i <= r; ++i)
#define lep(i, l, r) for(int i = l; i < r; ++i)
#define irep(i, r, l) for(int i = r; i >= l; --i)
#define ilep(i, r, l) for(int i = r; i > l; --i)
typedef long long ll;

struct edge {
    int head[MAX_N], to[MAX_N << 1], nxt[MAX_N << 1], val[MAX_N << 1], cap;
    edge () : cap(0) {}
    inline void addE (int u, int v, int w = 0) {
        nxt[++cap] = head[u];
        head[u] = cap;
        to[cap] = v;
        val[cap] = w;
    }
}E, en;

int N, M, res = 1, ans = 0, totv, tote, now;
int dep[MAX_N], st[MAX_N], top, fa[MAX_N], val[MAX_N];
int vert[MAX_N], tp;
int deg[MAX_N], fac[MAX_N], ifac[MAX_N];
bool vis[MAX_N], vise[MAX_N], onc[MAX_N];

bool findcir (int x, int las = 0) {
    if (vis[x]) return false;
    totv++;
    vis[x] = true;
    vert[++tp] = x;
    bool flag = false;
    for (int i = E.head[x]; i; i = E.nxt[i]) {
        // printf("%d %d\n", x, E.to[i]);
        tote++;
        if (!vis[E.to[i]]) {
            if (findcir(E.to[i], x)) flag = true;
        } else if (E.to[i] != las && x != now) {
            // printf("%d %d\n", x, E.to[i]);
            flag = true;
            now = E.to[i];
        }
    }
    // printf("%d %d\n", x, flag);
    if (flag) onc[x] = true, st[++top] = x;
    // printf("%d %d\n", x, flag);
    return flag && (now != x);
}

void getd (int x, int las) {
    for (int i = E.head[x]; i; i = E.nxt[i])
        if (E.to[i] != las && !onc[E.to[i]]) {
            dep[E.to[i]] = dep[x] + 1;
            fa[E.to[i]] = x, val[E.to[i]] = E.val[i];
            getd(E.to[i], x);
        }
}

void getc (int x, int las, int t) {
    for (int i = E.head[x], v = E.to[i]; i; i = E.nxt[i], v = E.to[i])
        if (onc[v] && v != las) {
            fa[x] = v, val[x] = E.val[i];
            if (v != t) getc(v, x, t);
        }
}

int f[MAX_N], sz[MAX_N];

inline int C (int x, int y) {
    if (x < y || y < 0) return 0;
    return mul(fac[x], mul(ifac[y], ifac[x - y]));
}

void dp (int x) {
    // printf("%d\n", x);
    sz[x] = f[x] = 1;
    for (int i = en.head[x], v = en.to[i]; i; i = en.nxt[i], v = en.to[i]) {
        dp(v);
        f[x] = mul(f[x], mul(f[v], ifac[sz[v]]));
        // f[x] = mul(mul(f[x], f[v]), C(sz[x] + sz[v] - 1, sz[v]));
        sz[x] += sz[v];
    }
    f[x] = mul(f[x], fac[sz[x] - 1]);
}

inline void solve () {
    en.cap = 0;
    rep (i, 1, tp) en.head[vert[i]] = 0;
    en.head[M + 1] = 0;
    rep (t, 1, tp) {
        int x = vert[t];
        // printf("%d %d %d\n", x, fa[x], val[x]);
        for (int i = E.head[x], v = E.to[i]; i; i = E.nxt[i], v = E.to[i])
            if (v != fa[x] && E.val[i] < val[x]) {
                // printf("%d %d\n", x, E.to[i]);
                en.addE(x, E.to[i]);
                deg[E.to[i]]++;
            }
    }
    rep (i, 1, tp) if (!deg[vert[i]]) {
        en.addE(M + 1, vert[i]);
    }
    rep (i, 1, tp) deg[vert[i]] = 0;
    dp(M + 1);
    inc(ans, f[M + 1]);
}

inline void init () {
    fac[0] = ifac[0] = 1;
    rep (i, 1, M) fac[i] = mul(fac[i - 1], i);
    ifac[M] = qpow(fac[M], MOD - 2);
    irep (i, M, 2) ifac[i - 1] = mul(ifac[i], i);
}

int main () {
    read(N);
    M = 2 * N;
    init();
    int x, y;
    rep (i, 1, M) {
        read(x), read(y);
        // printf("%d %d %d\n", x, y + N, x + y);
        E.addE(x, y + N, x + y);
        E.addE(y + N, x, x + y);
    }
    int tot = 0;
    rep (i, 1, N * 2) if (!vis[i]) {
        totv = tote = top = tp = 0;
        findcir(i);
        // printf("%d %d\n", tote, totv);
        // rep (i, 1, top) printf("%d ", st[i]); puts("");
        // break;
        if (tote != totv * 2) { res = 0; break; }
        tot += totv;
        rep (i, 1, top) {
            dep[st[i]] = 0;
            getd(st[i], 0);
        }
        ans = 0;
        int u = st[1];
        for (int j = E.head[u]; j; j = E.nxt[j])
            if (onc[E.to[j]]) {
                // printf("%d %d\n", u, E.to[j]);
                fa[u] = E.to[j];
                val[u] = E.val[j];
                getc(E.to[j], u, u);
                solve();
                // printf("%d\n", ans);
                // return 0;
            }
        // printf("%d\n", ans);
        res = mul(mul(res, ans), C(tot, totv));
    }
    printf("%d\n", res);
    return 0;
}


Measured memory_bytes: 22784