题意:给定一个长度为n的排列p,每次操作可以交换排列中的任意两数,至少需要m次可以将排列p变成单调递增序列
求有多少种操作方式,只交换m次使得p变成单调递增序列
解:
显然,将p[i]与i建一条边,则序列变成了多个环,一个大小为n的环至少需要n-1次操作使其变成n个
自环(也很显然, 也可用数学归纳法证明,将大小为n的环拆成两个更小的环···)
设将一个大小为n的环拆成一个大小为x,另一个大小为y的环有 T(x,y)种方法则
当 x = y 时,T = n / 2,否则 T = n (可画图理解)
设将一个大小为n的环拆成n个自环的方案数有F(n)中则
除法部分为多重集的排列数,即将F(x)和 F(y)进行排列
设序列p一共有大小为 的k个环, 则
n - k 是一共需要操作的次数,也就是m
//https://ac.nowcoder.com/acm/contest/1026/B
#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const ll mod = 1e9 + 9;
const int N = 1e5 + 5;
ll n, p[N], fac[N], invfac[N], F[N];
ll qpow(ll base, ll pow)
{
ll ans = 1;
while (pow)
{
if (pow & 1)
ans = ans * base % mod;
pow >>= 1;
base = base * base % mod;
}
return ans;
}
void init()
{
fac[0] = invfac[0] = 1;
for (int i = 1; i < N; i++)
{
fac[i] = fac[i - 1] * i % mod;
invfac[i] = qpow(fac[i], mod - 2);
}
F[1] = 1;
/*
for (int i = 2; i < N; i++)//O(n^2)
{
for (ll x = 1; x <= i / 2; x++)
{
ll y = i - x;
ll T = (x == y ? i / 2 : i);
F[i] = (F[i] + T * F[x] % mod * F[y] % mod * fac[i - 2] % mod * invfac[x - 1] % mod * invfac[y - 1] % mod) % mod;
}
}
*/
for (int i = 2; i < N; i++)//找规律发现F(i) = i ^ (i - 2), 复杂度O(n)
F[i] = qpow(i, i - 2);
}
int fa[N], sz[N], vis[N], l[N];
int find(int x)
{
return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
return;
if (sz[x] > sz[y])
swap(x, y);
fa[x] = y;
sz[y] += sz[x];
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> p[i];
for (int i = 1; i <= n; i++)
{
fa[i] = i;
sz[i] = 1;
vis[i] = 0;
}
for (int i = 1; i <= n; i++)
merge(i, p[i]);
int k = 0;
for (int i = 1; i <= n; i++)
{
if (!vis[find(i)])
{
vis[find(i)] = 1;
l[++k] = sz[find(i)];
}
}
ll ans = fac[n - k];
for (int i = 1; i <= k; i++)
ans = ans * F[l[i]] % mod * invfac[l[i] - 1] % mod;
cout << ans << '\n';
}
signed main()
{
IOS;
int t = 1;
init();
cin >> t;
while (t--)
solve();
return 0;
}