//Have a nice day:)
#include<bits/stdc++.h>
using namespace std;
#define LL long long

inline int read();

const int maxn=2e5+2;
const int mod=1e9+7;

int n, c, N, tp, cnt_edge, cnt_node, ans, total, cnt_cir;
bool fail;
int stk[maxn], inv[maxn], fac[maxn], siz[maxn], cir[maxn], all[maxn], mx[maxn], must[maxn];
int he[maxn], ne[maxn<<1], to[maxn<<1], w[maxn<<1];
bool vis[maxn], col[maxn<<1], yes[maxn];

int Pow(int a, int b) 
{
    int u=1;
    for(; b; b>>=1, a=1ll*a*a%mod) if(b&1) u=1ll*u*a%mod;
    return u;
}

void Init()
{
    inv[0]=fac[0]=1;
    for(int i=1; i<=N; ++i) fac[i]=1ll*fac[i-1]*i%mod;
    inv[N]=Pow(fac[N], mod-2);
    for(int i=N-1; i>=1; --i) inv[i]=1ll*inv[i+1]*(i+1)%mod;
}

int C(int a, int b)
{
    if(a>b || a<0 || b<0) return 0;
    return 1ll*fac[b]*inv[a]%mod*inv[b-a]%mod;
}

void Add_Edge(int x, int y, int z)
{
    ne[++c]=he[x]; he[x]=c; to[c]=y; w[c]=z;
    ne[++c]=he[y]; he[y]=c; to[c]=x; w[c]=z;
    mx[x]=max(mx[x], z); mx[y]=max(mx[y], z);
}

void Find_Circle(int p, int f)
{
    vis[p]=1; all[++cnt_node]=p;  

    for(int i=he[p]; i; i=ne[i])
    {
        if(to[i]==f || col[i]) continue;
        ++cnt_edge; stk[++tp]=i;
        
        if(vis[to[i]]) 
        {
            if(cnt_cir) continue;
            
            do
            {
                int edge=stk[tp]; --tp;
                col[edge]=col[edge^1]=1;
                cir[++cnt_cir]=edge;
            }
            while(to[stk[tp+1]^1]!=to[i]);

            continue;
        }
        
        Find_Circle(to[i], p); 
        if(!col[i]) tp--;
    }
}

void Must_Link(int p, int f)
{
    for(int i=he[p]; i; i=ne[i])
    {
        if(to[i]==f || col[i]) continue;
        must[to[i]]=i^1; 
        Must_Link(to[i], p);
    }
}

int fa[maxn];
int Count(int p, int f)
{
    int now=1; siz[p]=0;
    for(int i=he[p]; i; i=ne[i])
    {
        if(to[i]==f || fa[to[i]]!=p) continue;
        int way=Count(to[i], p);
        siz[p]+=siz[to[i]];
        now=1ll*C(siz[to[i]], siz[p])*way%mod*now%mod;
    }
    ++siz[p];
    return now;
}

void Subtree_Build()
{
    for(int i=1; i<=cnt_node; ++i) fa[all[i]]=0;
    for(int i=1; i<=cnt_node; ++i)
    {
        for(int j=he[all[i]]; j; j=ne[j])
        {
            if(w[j]<w[must[all[i]]]) fa[to[j]]=all[i];
        }
    }
}

void Work()
{
    n=read(); N=n<<1; c=ans=1; Init();
    for(int i=1; i<=N; ++i) 
    {
        int x=read(), y=read();
        Add_Edge(x, y+n, x+y);
    }

    for(int i=1; i<=N; ++i)
    {
        if(vis[i]) continue;

        tp=0; cnt_node=cnt_edge=cnt_cir=0;
        Find_Circle(i, 0);
        if(cnt_node!=cnt_edge) 
        {
            fail=1; break;
        }

        for(int j=1; j<=cnt_cir; ++j) Must_Link(to[cir[j]], 0);
        for(int j=1; j<=cnt_cir; ++j) must[to[cir[j]]]=cir[j];

        Subtree_Build();
        
        int now1=1, cnt1=0;
        for(int j=1; j<=cnt_node; ++j)
        {
            if(fa[all[j]]) continue;
            now1=1ll*now1*Count(all[j], 0)%mod*C(siz[all[j]], siz[all[j]]+cnt1)%mod;
            cnt1+=siz[all[j]];
        }

        for(int j=1; j<=cnt_cir; ++j) must[to[cir[j]^1]]=cir[j]^1;

        Subtree_Build();
        
        int now2=1, cnt2=0;
        for(int j=1; j<=cnt_node; ++j)
        {
            if(fa[all[j]]) continue;
            now2=1ll*now2*Count(all[j], 0)%mod*C(siz[all[j]], siz[all[j]]+cnt2)%mod;
            cnt2+=siz[all[j]];
        }

        total+=cnt_node;
        ans=1ll*C(cnt_node, total)*ans%mod*((now1+now2)%mod)%mod;
    }

    if(fail) printf("0");
    else printf("%d", ans);
}

int main()
{
   

    Work();

    return 0;
}

inline int read()
{
    char c; bool type=1;
    while((c=getchar())<'0' || c>'9')
        if(c=='-') type=0;
    int ans=c^48;
    while((c=getchar())>='0' && c<='9')
        ans=(ans<<3)+(ans<<1)+(c^48);
    return type?ans:-ans;
}



Measured memory_bytes: 14464