Counting Swaps[CH3602]

欢迎大家访问我的老师的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 124,36和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/2nnx=ynx=y}

另外,二者各自变为自环的方法书为 F x F_x Fx F y F_y Fy,步数为 x − 1 x-1 x1 y − 1 y-1 y1

根据多重集的排列数、加法原理和乘法原理:
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=nT(x,y)FxFy(x1)!(y1)!(n2)!

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

如果最初的排列 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)!} Fl1Fl2...Flk(l11)!(l21)!...(lk1)!(nk)!

稍微解释一下为什么还要乘 ( n − k ) ! ( l 1 − 1 ) ! ( l 2 − 1 ) ! . . . ( l k − 1 ) ! \large\frac{(n-k)!}{(l_1-1)!(l_2-1)!...(l_k-1)!} (l11)!(l21)!...(lk1)!(nk)!,因为我们要 n − k n-k nk步才能变成n个自环,所以相当于我们有 n − k n-k nk个操作,操作如果换一下顺序,就有 ( n − k ) ! (n-k)! (nk)!种,但其中有 ( l 1 − 1 ) ! ( l 2 − 1 ) ! . . . ( l k − 1 ) ! (l_1-1)!(l_2-1)!...(l_k-1)! (l11)!(l21)!...(lk1)!种属于同一类,因为相同操作不分先后。

我们可以递推前几项找规律,得到通项公式 F n = n n − 2 F_n=n^{n-2} Fn=nn2

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值