这个式子就不再推导了,书上写的很明确,但感觉解释的不是很明白,别的博客都是直接抄的原文,于是写一下自己的理解。
解释下这个式子
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;
}
}