洛谷 「CF1534C Little Alawn's Puzzle「」
\(\texttt{Description}\)
一个 \(2\times n\) 的矩阵,可以操作任意次,每次操作是交换某一列上的 \(2\) 个数字,经过操作之后,有多少种同一行没有相同数字的情况。方案数对 \(10^9+7\) 取模。
\(\texttt{Solution}\)
这是一道好题(当然是对于我这种屑来说)。
-
我们知道,我们交换的一定是某些整个的循环置换。
-
循环置换的意思就是在这个置换里面的每个数都出现了 \(2\) 次,可以理解为是一个封闭的块。
-
假设有 \(cnt\) 个循环置换,那么答案其实就是 \(\sum\limits_{i=0}^{cnt}C_{cnt}^i\),这个应该是很好理解(代码实现中写的是这种)。但是还有另外一种简单的求法,就是 \(2^{cnt}\),为什么呢?因为每个都有选或不选两种情况。
-
至于如何求出 \(cnt\),有两种方法:
-
直接 dfs,这里我选择了 dfs。
-
并查集,将给出的矩阵每列的两个数合并,再统计连通块数量即可。
-
时间复杂度:\(\mathcal{O}(n\log n)\)
\(\texttt{Code}\)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <map>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
inline int Min (int x, int y) {return x < y ? x : y;}
inline int Max (int x, int y) {return x > y ? x : y;}
int a[400005], b[400005];
bool vis[400005];
map<int, int> mp;
inline ll quickPow (ll a, ll k, ll p) {
ll res = 1;
a %= p;
while (k) {
if (k & 1)
res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
inline ll inv (ll x, ll p) {
return quickPow(x, p - 2, p);
}
inline void dfs (int i) {
if (vis[b[i]])
return;
vis[b[i]] = true;
dfs(mp[b[i]]);
}
int main () {
int T;
scanf("%d", &T);
while (T--) {
int n, cnt = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), mp[a[i]] = i;
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
memset(vis, false, sizeof(vis));
for (int i = 1; i <= n; i++)
if (!vis[a[i]]) {
vis[a[i]] = true;
dfs(i);
cnt++;
}
ll ans = 1, tot = 1ll * cnt, fac = 1;
for (int i = 1; i <= cnt; i++) {
fac = fac * i % mod;
ans = (ans + tot * inv(fac, mod) % mod) % mod;
tot = tot * (cnt - i) % mod;
}
printf("%lld\n", ans);
}
return 0;
}

浙公网安备 33010602011771号