首先,我们可以想想对方怎么让我们必败。因为最后要组成排列,所以对方可以一直让我们取不到一个数。
所以,我们需要每一个数都在某一个位置上“一定取得到”,即对于每一个 ,都存在一个 ,使 中,至少有两个为 。
那么要如何计算方案数呢?我们会发现,如果 ,那么对于这个 ,能“一定取得到”的只能是这两个数中的一个。于是我们可以建出一张图,对于每个 ,在 和 之间连一条边。每次确定一个 就相当于是删掉点 以及它的一条边。答案就是删的方案数。(无序)
怎样计算方案数呢?我们会发现,如果有解,建出的图一定是类似一个内向基环树森林。对于那些不在环上的点,可以用拓扑排序一个一个一个删掉,它们删的方式都是惟一的。至于在环上的点,如果是自环,结合原本的题意可知,这一位上放 至 的任意数都行,否则,一个环上的每一个点都和它“左边或右边”的边一起删掉,每个环都会使方案数 。
形式化地说,设有 个自环, 个非自环的环,答案即为 。
code:
int n,m,cnt,d[N],in[N];
int l,r,q[N];
int tot,head[N];struct node{int to,nxt;}e[N<<1];
inline void add(int u,int v){e[++tot]={v,head[u]},head[u]=tot;}
void dfs(int u){
in[u]=-1,cnt++;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(~in[v])dfs(v);
}
}
void solve(){
scanf("%d",&n);
ll ans=1;mems(in,0),mems(head,0),tot=0,l=1,r=0;
for(int i=1;i<=n;i++)scanf("%d",&d[i]);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
if(d[i]==x)ans=ans*n%mod;
add(x,d[i]),add(d[i],x);
in[x]++,in[d[i]]++;
}
for(int i=1;i<=n;i++){
if(in[i]==1)q[++r]=i;
if(!in[i]){puts("0");return;}
}
while(l<=r){
int u=q[l++];in[u]=-1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(in[v]==-1)continue;
in[v]--;
if(!in[v]){puts("0");return;}
if(in[v]==1)q[++r]=v;
}
}
for(int i=1;i<=n;i++){
if(~in[i]){
cnt=0,dfs(i);
if(cnt>1)ans=ans*2%mod;
}
}
printf("%lld\n",ans);
}
signed main(){
int t=1;
scanf("%d",&t);
while(t--)solve();
}