题解 P4778【Counting swaps】

link 的抄写。

啊,还是不懂,以后再来看了。

problem

一次操作指随意选定 \(x,y\) 并交换 \(a_x,a_y\),请问有多少种方案,能用最少的操作次数重排一个排列 \(a\)\(n\leq 10^5,P=10^9+7\)

solution 0

连边 \(i\to a_i\),这是一张可以发起反攻的有向图,里面有很多个环,我们称这样的环为置换环。

引理一:对于一个 \(n\) 的置换环,完成对它的排序需要 \(n-1\) 次操作。

我们求出一个 \(f_i\) 表示大小为 \(i\) 的置换环排序的方案数,这是后文重点讨论内容,略之。

求出所有的置换环。因为环与环之间互不影响,相当于操作序列也互不影响,我们可以随意打乱环与环的相对顺序,这是多重集排列数(介绍见 ZROI371),记一个环的大小为 \(b_i\),则:

\[ans=\prod_i f_{b_i}\cdot \frac{\left(\sum_i(b_i-1)\right)!}{\prod_i (b_i-1)!}. \]

到这里复杂度还是线性的。

solution 1

首先前人结论先摆出来:\(f_i=i^{i-2}\)

求法一

一次交换后将大小为 \(n\) 的环分成大小分别为 \(i,n-i\) 的环,分别枚举:

  • 两个操作序列交错插入,是多重集排列数
  • 枚举交换哪一个数字
    因此我们有:

\[f_1=1,f_n=\sum_{2i\leq n}\binom{n-2}{i-1}f_i f_{n-i}(2i==n?\frac{n}{2}:n). \]

求法二

这样交换,每一次交换都连边,最终连出一棵树。然后容易证明每棵树和每个合法操作方案一一对应。

所以答案为有标号无根树计数:\(n^{n-2}\)

code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=1e9+9;
LL qpow(LL a,LL b,int p=P){LL r=1;for(a%=P;b;b>>=1,a=a*a%p) if(b&1) r=r*a%p; return r;}
template<int N,int P> struct C_prime{
	LL fac[N+10],ifac[N+10];
	C_prime(){
		for(int i=fac[0]=ifac[0]=1;i<=N;i++) fac[i]=fac[i-1]*i%P;
		ifac[N]=qpow(fac[N],P-2,P);
		for(int i=N-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%P;
	}
	LL operator()(int n,int m){return fac[n]*ifac[n-m]%P*ifac[m];}
};
int n,a[100010],b[100010],cnt;
bool vis[100010];
LL ans=1,f[100010];
C_prime<100010,P> C;
int dfs(int x){
	if(vis[x]) return 0;
	vis[x]=1;
	return dfs(a[x])+1;
}
int mian(){
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		if(vis[i]) continue;
		int res=dfs(i);
		if(res>1){
			ans=ans*f[res]%P;
			ans=ans*C.ifac[res-1]%P;
		}
		cnt++;
	}
	ans=ans*C.fac[n-cnt]%P;
	printf("%lld\n",ans);
	return 0;
}
void reset(){
	memset(vis,0,sizeof vis);
	cnt=0,ans=1;
}
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	f[1]=1;
	for(int i=2;i<=1e5;i++) f[i]=qpow(i,i-2,P);
	for(scanf("%*d");~scanf("%d",&n);reset(),mian());
	return 0;
}
posted @ 2022-11-10 23:16  caijianhong  阅读(79)  评论(0编辑  收藏  举报