欢迎大家访问我的老师的OJ———caioj.cn
题面描述
思路
对于一个排列 p 1 , p 2 , . . . p n p_1,p_2,...p_n p1,p2,...pn,如果从每个 i i i向 p i p_i pi连一条边,那么可以得到 n n n个点 n n n条边的图,并且这张图由若干个环构成。
列入排列
2
,
4
,
6
,
1
,
5
,
3
2,4,6,1,5,3
2,4,6,1,5,3对应下图,由
1
−
2
−
4
,
3
−
6
1-2-4,3-6
1−2−4,3−6和5三个环。
显然,我们最后要得到
n
n
n个自环,即排列
1
,
2
,
.
.
.
,
n
1,2,...,n
1,2,...,n.
引理:
把一个长度为n的环变成n个自环最少需要n-1次交换操作。
证明:
显然,每一次操作,最多只能将一个环变成两个环,所以将一个长度为n的环变成n个自环,
最少也需要n-1次交换操作。
证毕。
设 F n F_n Fn表示用最少的步数把一个长度为 n n n的环变成 n n n个自环,共有多少种方法。把一个长度为 n n n的环变成 n n n个自环的过程中,把该环拆成长度为 x x x和 y y y的两个环,其中 x + y = n x+y=n x+y=n。设 T ( x , y ) T(x,y) T(x,y)表示有多少种交换方法可以把长度为n的环变成长度为 x x x和 y y y的两个环,下面我们进行分类讨论:
当n为偶数并且x=y时,
如图,排列4,1,2,3构成一个长度为4的环,我们要将其分成2个长度为2的环。
有2种方法:
另外一种为 4 3 2 1
则 T ( x , y ) = n / 2 T(x,y)=n/2 T(x,y)=n/2( n n n为偶数并且 x = y x=y x=y)
当 n n n是奇数或 x ≠ y x \ne y x=y时, T ( x , y ) = n T(x,y)=n T(x,y)=n,如何构造参照上图(在下太懒了)
综上所述,
T
(
x
,
y
)
=
{
n
/
2
n
是
偶
数
并
且
x
=
y
n
n
是
奇
数
或
x
≠
y
}
T(x,y)=\begin{Bmatrix}n/2&n是偶数并且x=y\\n&n是奇数或x\ne y\end{Bmatrix}
T(x,y)={n/2nn是偶数并且x=yn是奇数或x=y}
另外,二者各自变为自环的方法书为 F x F_x Fx和 F y F_y Fy,步数为 x − 1 x-1 x−1和 y − 1 y-1 y−1。
根据多重集的排列数、加法原理和乘法原理:
F
n
=
∑
x
+
y
=
n
T
(
x
,
y
)
∗
F
x
∗
F
y
∗
(
n
−
2
)
!
(
x
−
1
)
!
(
y
−
1
)
!
F_n=\sum_{x+y=n}T(x,y)*F_x*F_y*\frac{(n-2)!}{(x-1)!(y-1)!}
Fn=x+y=n∑T(x,y)∗Fx∗Fy∗(x−1)!(y−1)!(n−2)!
(
n
−
2
)
!
(
x
−
1
)
!
(
y
−
1
)
!
\large\frac{(n-2)!}{(x-1)!(y-1)!}
(x−1)!(y−1)!(n−2)!相当于有
n
−
2
n-2
n−2个操作,其中有
x
−
1
x-1
x−1个为1,
y
−
1
y-1
y−1个为0,
其实相当于求多重集
{
(
x
−
1
)
⋅
1
(
y
−
1
)
⋅
0
}
\begin{Bmatrix}(x-1)\cdot 1&(y-1)\cdot 0\end{Bmatrix}
{(x−1)⋅1(y−1)⋅0}的全排列数,即
(
n
−
2
)
!
(
x
−
1
)
!
(
y
−
1
)
!
\large\frac{(n-2)!}{(x-1)!(y-1)!}
(x−1)!(y−1)!(n−2)!
如果最初的排列
p
1
,
p
2
,
.
.
.
,
p
n
p_1,p_2,...,p_n
p1,p2,...,pn,由长度为
l
1
,
l
2
,
.
.
.
l
k
l_1,l_2,...l_k
l1,l2,...lk的k个环构成,其中
l
1
+
l
2
+
.
.
.
+
l
k
=
n
l_1+l_2+...+l_k=n
l1+l2+...+lk=n,根据多项式排列数,那么最终答案就是:
F
l
1
∗
F
l
2
∗
.
.
.
∗
F
l
k
∗
(
n
−
k
)
!
(
l
1
−
1
)
!
(
l
2
−
1
)
!
.
.
.
(
l
k
−
1
)
!
F_{l_1}*F_{l_2}*...*F_{l_k}*\frac{(n-k)!}{(l_1-1)!(l_2-1)!...(l_k-1)!}
Fl1∗Fl2∗...∗Flk∗(l1−1)!(l2−1)!...(lk−1)!(n−k)!
稍微解释一下为什么还要乘 ( n − k ) ! ( l 1 − 1 ) ! ( l 2 − 1 ) ! . . . ( l k − 1 ) ! \large\frac{(n-k)!}{(l_1-1)!(l_2-1)!...(l_k-1)!} (l1−1)!(l2−1)!...(lk−1)!(n−k)!,因为我们要 n − k n-k n−k步才能变成n个自环,所以相当于我们有 n − k n-k n−k个操作,操作如果换一下顺序,就有 ( n − k ) ! (n-k)! (n−k)!种,但其中有 ( l 1 − 1 ) ! ( l 2 − 1 ) ! . . . ( l k − 1 ) ! (l_1-1)!(l_2-1)!...(l_k-1)! (l1−1)!(l2−1)!...(lk−1)!种属于同一类,因为相同操作不分先后。
我们可以递推前几项找规律,得到通项公式 F n = n n − 2 F_n=n^{n-2} Fn=nn−2
AC code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define ll long long
#define gc getchar()
using namespace std;
const ll mod=1e9+9;
const int N=1e5+10;
inline void qr(int &x)
{
x=0;int f=1;char c=gc;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
x*=f;
}
void qw(ll x)
{
if(x/10)qw(x/10);
putchar(x%10+48);
}
inline ll pow_mod(ll a,ll b)
{
ll ans=1;a%=mod;
while(b)
{
if(b&1)ans=ans*a%mod;
b>>=1;a=a*a%mod;
}
return ans;
}
ll c[N];int p[N];bool v[N];
int main()
{
c[0]=1;
for(int i=1;i<=N;i++)c[i]=c[i-1]*i%mod;
int t;qr(t);
while(t--)
{
int n;qr(n);ll ans=1;
for(int i=1;i<=n;i++)qr(p[i]),v[i]=0;
int cnt=0;
for(int i=1;i<=n;i++)
{
if(v[i])continue;
v[i]=1;
int len=1;cnt++;
for(int j=p[i];j!=i;j=p[j])++len,v[j]=1;
if(len>2)ans=ans*pow_mod(len,len-2)%mod;
ans=ans*pow_mod(c[len-1],mod-2)%mod;
}
ans=ans*c[n-cnt]%mod;
qw(ans);puts("");
}
return 0;
}