P6276 [USACO20OPEN] Exercise P 题解

传送门

Statement

求所有长度为 \(n\) 的排列的所有置换环的长度的最小公倍数的乘积。

\(n\le 7500\)

Solution

显然有:

\[\text{答案}=\prod_{p^k, p\in \operatorname{prime}}p^{\text{存在一个置换环的长度为} p^k \text{的倍数的排列的个数}} \]

枚举 \(p^k\),记 \(x=p^k\),考虑怎么计算 “存在一个置换环的长度为 \(p^k\) 的倍数的排列的个数”。发现直接求不好求,正难则反,考虑求其补集。

\(f_n\) 表示 “不存在一个置换环的长度为 \(p^k\) 的倍数的排列的个数”。发现还是不好求,于是再做一次减法。记 \(g_n\) 表示 “所有置换环的长度为 \(p^k\) 的倍数且长度为 \(n\) 的排列的个数”。易得:

\[g_n=\sum_{x\le i\le n,x|i}{n-1\choose i-1}(i-1)!g_{n-i} \]

\[f_n=n!-\sum_{x\le i\le n,x|i}{n\choose i}g_i\times f_{n-i} \]

那么对答案的贡献就是 \(\times p^{n!-f_n}\)

发现 \(g_n\)\(f_n\) 都只有 \(\lfloor\frac{n}{x}\rfloor\) 个的值不为 \(0\),总时间复杂度 \(\le O(\sum_{x=1}^n\frac{n^2}{x^2})=O(n^2)\)

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 7503
#define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
int n,t,md,md1,d[mxn],p[mxn],f[mxn],g[mxn],c[mxn][mxn];
ll ans,fac[mxn];
int power(int x,int y){
	int ans=1;
	for(;y;y>>=1){
		if(y&1)ans=(ll)ans*x%md;
		x=(ll)x*x%md;
	}
	return ans;
}
signed main(){
	cin>>n>>md,md1=md-1;
	fac[0]=1;
	rep(i,1,n)fac[i]=fac[i-1]*i%md1;
	c[0][0]=1;
	rep(i,1,n){
		c[i][0]=1;
		rep(j,1,i)c[i][j]=(c[i-1][j-1]+c[i-1][j])%md1;
	}
	rep(i,2,n){
		if(!d[i])d[i]=p[++t]=i;
		rep(j,1,t){
			if(p[j]>d[i]||p[j]>n/i)break;
			d[i*p[j]]=p[j];
		}
	}
	ans=1;
	rep(i,2,n)if(d[i]==i){
		int x=i;
		while(1){
			g[0]=1;
			for(int i=x;i<=n;i+=x){
				g[i]=0;
				for(int j=x;j<=i;j+=x){
					g[i]=(g[i]+g[i-j]*fac[j-1]%md1*c[i-1][j-1])%md1;
				}
			}
			f[0]=1;
			rep(i,1,n)if((n-i)%x==0){
				f[i]=fac[i];
				for(int j=x;j<=i;j+=x)f[i]=(f[i]-(ll)f[i-j]*g[j]%md1*c[i][j])%md1;
			}
			ans=ans*power(i,(fac[n]-f[n]+md1)%md1)%md;
			if((ll)x*i>n)break;
			x*=i;
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2023-10-28 23:40  zifanwang  阅读(10)  评论(0编辑  收藏  举报  来源