這個式子就不再推導了,書上寫的很明確,但感覺解釋的不是很明白,別的博客都是直接抄的原文,於是寫一下自己的理解。
解釋下這個式子
1、爲什麼不同x,y之間可以直接相加。
第一步的交換是把大環 i 拆成 x,y 兩個小環,不同的 x,y 第一步當然不同。那麼無論後面怎麼排,第一步不同,相互之間肯定無重複。
2、爲什麼可以直接 。
同樣的x,y,環裏的數不同,那麼在拆 i 的時候第一步一定不同,同上。
3、爲什麼有多重集。
對於拆成的兩個環,自己需要多少步形成自環x,y之間是沒有關係的,所以是乘法原理。
這個時候有個問題,直接乘是先x再y的數量,對於x,y兩個環交叉進行的數量並沒有統計在內,對於每一種x的方案,y的方案,交叉進行的時候,我們不改變所有x環交換的相對順序,比如 假設x有3步,X1,X2,X3,我們在x環和y環組合一起的序列中,不改變X1,X2,X3的相對位置,X1還在X2前面,但有可能X1和X2之間插入了Y1。
這樣的話,就構成了一個多重集。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#define ms(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef double ab;
const int N=1e5+5;
const ll mod=1e9+9;
ll jc[N],jc_inv[N],f[N];
int n;
int a[N],vis[N];
vector<int> ans;
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x=1;
y=0;
return ;
}
exgcd(b,a%b,y,x);
y-=(a/b)*x;
}
ll qpow(ll a, ll b)
{
ll res=1;
while(b)
{
if(b&1)
res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
void init()
{
f[1]=jc[0]=jc_inv[0]=jc[1]=jc_inv[1]=1;
for(int i=2;i<=N-5;i++)
{
f[i]=qpow(i,i-2);
jc[i]=jc[i-1]*i%mod;
ll x,y;
exgcd(jc[i],mod,x,y);
jc_inv[i]=(x%mod+mod)%mod;
}
}
int main()
{
int T;
init();
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
ll ans=1;
int l=0;
for(int i=1,len=1;i<=n;i++,len=1)
{
if(vis[i]) continue;
vis[i]=1;
for(int j=a[i];!vis[j];j=a[j])
{
vis[j]=1;
++len;
}
ans=ans*f[len]%mod;
ans=ans*jc_inv[len-1]%mod;
++l;
}
ans=ans*jc[n-l]%mod;
printf("%lld\n",ans);
for(int i=1;i<=n;i++)
vis[i]=0;
}
}