Counting Swaps[CH3602]

版权声明:作为一个蒟蒻,转载时请通知我这个蒟蒻 https://blog.csdn.net/zyszlb2003/article/details/89633759

欢迎大家访问我的老师的OJ———caioj.cn

题面描述

传送门

思路

对于一个排列 p 1 , p 2 , . . . p n p_1,p_2,...p_n ,如果从每个 i i p i p_i 连一条边,那么可以得到 n n 个点 n n 条边的图,并且这张图由若干个环构成。

列入排列 2 , 4 , 6 , 1 , 5 , 3 2,4,6,1,5,3 对应下图,由 1 2 4 , 3 6 1-2-4,3-6 和5三个环。
在这里插入图片描述
显然,我们最后要得到 n n 个自环,即排列 1 , 2 , . . . , n 1,2,...,n .

引理:

把一个长度为n的环变成n个自环最少需要n-1次交换操作。

证明:

显然,每一次操作,最多只能将一个环变成两个环,所以将一个长度为n的环变成n个自环,
最少也需要n-1次交换操作。

证毕。

F n F_n 表示用最少的步数把一个长度为 n n 的环变成 n n 个自环,共有多少种方法。把一个长度为 n n 的环变成 n n 个自环的过程中,把该环拆成长度为 x x y y 的两个环,其中 x + y = n x+y=n 。设 T ( x , y ) T(x,y) 表示有多少种交换方法可以把长度为n的环变成长度为 x x y y 的两个环,下面我们进行分类讨论:

当n为偶数并且x=y时,

如图,排列4,1,2,3构成一个长度为4的环,我们要将其分成2个长度为2的环。
在这里插入图片描述
有2种方法:
在这里插入图片描述

T ( x , y ) = n / 2 T(x,y)=n/2 ( n n 为偶数并且 x = y x=y )

n n 是奇数或 x y x \ne y 时, 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}

另外,二者各自变为自环的方法书为 F x F_x F y F_y ,步数为 x 1 x-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)!}

( n 2 ) ! ( x 1 ) ! ( y 1 ) ! \large\frac{(n-2)!}{(x-1)!(y-1)!} 相当于有 n 2 n-2 个操作,其中有 x 1 x-1 个为1, y 1 y-1 个为0,
其实相当于求多重集 { ( x 1 ) 1 ( y 1 ) 0 } \begin{Bmatrix}(x-1)\cdot 1&(y-1)\cdot 0\end{Bmatrix} 的全排列数,即 ( n 2 ) ! ( x 1 ) ! ( y 1 ) ! \large\frac{(n-2)!}{(x-1)!(y-1)!}

如果最初的排列 p 1 , p 2 , . . . , p n p_1,p_2,...,p_n ,由长度为 l 1 , l 2 , . . . l k l_1,l_2,...l_k 的k个环构成,其中 l 1 + l 2 + . . . + l k = n l_1+l_2+...+l_k=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)!}

稍微解释一下为什么还要乘 ( n k ) ! ( l 1 1 ) ! ( l 2 1 ) ! . . . ( l k 1 ) ! \large\frac{(n-k)!}{(l_1-1)!(l_2-1)!...(l_k-1)!} ,因为我们要 n k n-k 步才能变成n个自环,所以相当于我们有 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)! 种属于同一类,因为相同操作不分先后。

我们可以递推前几项找规律,得到通项公式 F n = n n 2 F_n=n^{n-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;
}

猜你喜欢

转载自blog.csdn.net/zyszlb2003/article/details/89633759