观察容易发现:按i->p[i]连边形成的图是一个由多个环组成的图。
目标是把图变成全部自环。
我们任意交换2个数,会把一个大环变成两个小环,且小环长度相加等于大环。
我们利用组合数学求出Fi,环的大小为i,变成全部自环的方案数。
再用多重集排列,求出整个图的方案数即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,-1,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}
*/
const int mod =1e9+9;
ll qpow (ll a,ll b)
{
if(b<=0)return 1;
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
int a[M],vs[M];
ll F[M],fac[M];
void work()
{
fac[0]=1;
for(int i=1;i<=1e5;i++)fac[i]=fac[i-1]*i%mod;
F[1]=F[2]=1;
for(int i=3;i<=1e5;i++)F[i]=qpow(i,i-2);
//打表观察发现:Fn=n^(n-2)
// for(ll i=3;i<=10;i++)
// {
// ll ans=0;
// for(ll x=1;x<=i/2;x++)//(x,y)(y,x)只算一次
// {
// ll y=i-x,tp=0;
// if(x==y)tp=x;
// else tp=i;
// ans=(ans+F[x]*F[y]%mod*fac[i-2]%mod*qpow(fac[x-1]*fac[y-1]%mod,mod-2)%mod*tp%mod);
// }
// F[i]=ans;
// }
// for(int i=1;i<=10;i++)
// cout<<i<<" "<<fac[i]<<" "<<F[i]<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
work();
cin>>t;
while(t--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
memset(vs,0,sizeof(vs));
vector<int>v;
for(int i=1;i<=n;i++)
{
if(vs[i]==1)continue;
int tp=i,nm=0;
while(vs[tp]==0)vs[tp]=1,tp=a[tp],nm++;
v.pb(nm);
}
ll ans=1,N=0,TP=1;
for(int i=0;i<v.size();i++)
{
N+=v[i]-1;//把所有环变成自环的最小步数
if(v[i]==1)continue;
ans=ans*F[v[i]]%mod;
TP=TP*fac[v[i]-1]%mod;//多重集排列
}
ans=ans*fac[N]%mod*qpow(TP,mod-2)%mod;
cout<<ans<<endl;
}
return 0;
}