Loading

【题解】[USACO20OPEN]Exercise P

综合数数题。技巧性很强。

转化一下,求所有长度为 \(n\) 的排列的置换环的长度的 \(\rm lcm\) 的乘积。

考虑转化为数数问题,求 \(x=\rm lcm\) 的排列个数。

还是不好求,转化为前缀和,求 \(x\mid \rm lcm\) 的排列个数。

这个形式就比较好看,因为这等价于对于每个质数幂,求 \(p^c \mid \rm lcm\) 的排列个数,记作 \(\mathcal{F}(p^c)\),那么答案就是 \(\prod\limits_{p,c}p^{\mathcal{F}(p^c)}\)

现在我们要求 \(\mathcal{F}(x)\) ,表示 \(x\mid \rm lcm\) 的排列个数,等价于存在一个置换环是 \(x\) 的倍数的排列个数。

存在至少一个 这个玩意不好求,我们求补集,即不存在长度为 \(x\) 的倍数的置换环的排列数。记作 \(f(x)\)

发现这 \(f(x)\) 还是不好求,我们考虑再求一次补集,即枚举长度为 \(x\) 的倍数的置换环长度之和,对应的方案数为 \(g(x)\)。那么我们不难得到转移方程。

\[f_i=i!-\sum\limits_{0\le j< i}\binom{i}{j}f_j\times g_{i-j} \]

那么 \(g(i)\) 的转移呢?首先 \(g(i)\)\(i\) 一定是 \(x\) 的倍数。我们枚举包括第一个点的置换长度,得到转移方程。

\[g_i=\sum\limits_{0\le j<i}\binom{i-1}{i-j-1}(i-j-1)!g_j \]

有值的 \(g\) 只有 \(\frac{n}{x}\) 个,所以有用的 \(f\) 也只有 \(\frac{n}{x}\)个,时间复杂度为 \(\mathcal{O}(n^2)\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 7505
using namespace std;
int n,P,Q,f[N],g[N],c[N][N],v[N],fac[N];
int Pow(int x,int y){int now=1;for(;y;y>>=1,x=1LL*x*x%P)if(y&1)now=1LL*now*x%P;return now;}
int calc(int x){
	g[0]=1;
	for(int i=x;i<=n;i+=x){
		g[i] = 0;
		for(int j=0;j<i;j+=x)g[i]=(g[i]+1LL*g[j]*c[i-1][i-j-1]%Q*fac[i-j-1])%Q;
	}
	int st = n % x;
	f[st] = fac[st] ;
	for(int i=st+x;i<=n;i+=x){
		f[i] = fac[i] ;
		for(int j=st;j<i;j+=x)f[i]=(f[i]-1LL*c[i][j]*f[j]%Q*g[i-j])%Q;
	}
	return (0LL+fac[n]-f[n]+Q)%Q;
}
int main(){
	scanf("%d%d",&n,&P);Q=P-1;
	rep(i,0,n){c[i][0]=1;rep(j,1,i)c[i][j]=(c[i-1][j]+c[i-1][j-1])%Q;}
	fac[0]=1;rep(i,1,n)fac[i]=1LL*fac[i-1]*i%Q;
	int ans=1;
	rep(i,2,n)if(!v[i]){
		for(int j=i*i;j<=n;j+=i)v[j]=1;
		for(int j=i;j<=n;j*=i)ans=1LL*ans*Pow(i,calc(j))%P;
	}
	printf("%d\n",ans);
	return 0;
} 
posted @ 2021-06-17 23:14  7KByte  阅读(57)  评论(0编辑  收藏  举报