问题描述
- 给你两列数,你可以每次将同一列的数上下交换,要你求出有多少种方案能使每一列每一行不出现重复的数字。
思路分析
- 如果把某一列两个数交换了的话,那么这两行必定有数出现两次,所以就要想办法把重复出现的数交换回去,这样的话,最后肯定会有一个正确方案。
- 我们如何来算这个东西呢?经过上面的解释我们可以想到,如果有
i
i
i对数能够通过交换使得有一个正确方案的话,我们只需要找到共有多少这样的对即可。
- 这样的对满足什么条件呢?其实我们把第一行该数连上与他同列的数,再将与他同列的数连上第一行这个数所在位置,然后再依此类推,我们总能找到一个结束点,实际上该题就是要我们求有几种这样的圈。
- 怎么求这样的圈呢?我用的是并查集找环,也就是把第二行连上第一行。然后判断即可。
- 然后就是求答案了,其实对于每个环,我们都有选或者不选的情况,所以答案就是
2
环
数
2^{环数}
2环数。
代码如下
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 4e5 + 10;
int fa[maxn];
int a[maxn];
int b[maxn];
int find(int x)
{
return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
ll qpow(ll a, ll b, ll p)
{
ll ans = 1;
while (b)
{
if (b & 1)
{
ans = ans * a % p;
}
a = a * a % p;
b >>= 1;
}
return ans;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
fa[i] = i;
}
int cnt = 0;
for (int i = 1; i <= n; i++)
{
cin >> b[i];
if (find(a[i]) == b[i])
{
cnt++;
}
else
fa[find(b[i])] = find(a[i]);
}
cout << qpow(2, cnt, 1000000007) << endl;
}
return 0;
}