题目描述
Farmer John(又)想到了一个新的奶牛晨练方案! 如同之前,Farmer John 的 N 头奶牛( 1 ≤ N ≤ 7500 )站成一排。对于 1 ≤ i ≤ N 的每一个 i ,从左往右第 i 头奶牛的编号为 i 。他告诉她们重复以下步骤,直到奶牛们与她们开始时的顺序相同。给定长为 N 的一个排列 A ,奶牛们改变她们的顺序,使得在改变之前从左往右第 i 头奶牛在改变之后为从左往右第 A i 头。 例如,如果A=(1,2,3,4,5),那么奶牛们总共进行一步就回到了同样的顺序。如果A=(2,3,1,5,4),那么奶牛们总共进行六步之后回到起始的顺序。每步之后奶牛们从左往右的顺序如下:
计算所有可能的 N ! 种长为 N 的排列 A 回到起始顺序需要的步数的乘积。
n<=7500
题解
直接算的话lcm太大了,考虑算p^k的贡献
(huge ans=prod{p^{sum{f(p^k)}}})
其中f(x)表示至少存在一个长度为x的倍数的环的方案数
容斥一下,系数为(-1)^环的个数
每次硬点当前环的开头为1号点,剩下的组合数排一下,最后乘上(环长-1)!表示环的连边方案
时间为(sum{(frac{n}{x})^2}),其中(sum{frac{1}{x^2}}=frac{pi^2}{6}),所以时间为O(n^2)
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define ll long long
#define file
using namespace std;
ll jc[7501],C[7501][7501],g[7501],s,ans;
int p[7501],n,mod,i,j,k,l,len;
bool f[7501];
ll qpower(ll a,int b) {ll ans=1;while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;}
void init()
{
int i,j,k,l;
fo(i,2,n)
{
if (!f[i]) p[++len]=i;
fo(j,1,len)
if (i*p[j]<=n)
{
f[i*p[j]]=1;
if (!(i%p[j])) break;
}
else break;
}
}
ll js(int s)
{
int N=n/s,i,j,k,l;
ll sum=0;
memset(g,0,(N+1)*8);g[0]=1;
fo(i,1,N)
{
fo(j,1,i)
g[i]=(g[i]-g[i-j]*C[i*s-1][j*s-1]%(mod-1)*jc[j*s-1])%(mod-1);
}
fo(i,0,N) sum=(sum+g[i]*C[n][i*s]%(mod-1)*jc[n-i*s])%(mod-1);
return (jc[n]-sum)%(mod-1);
}
int main()
{
freopen("exercise.in","r",stdin);
#ifdef file
freopen("exercise.out","w",stdout);
#endif
scanf("%d%d",&n,&mod);init();
jc[0]=1;fo(i,1,n) jc[i]=jc[i-1]*i%(mod-1);
fo(i,1,n)
{
C[i][0]=C[i][i]=1;
fo(j,1,i-1) C[i][j]=(C[i-1][j]+C[i-1][j-1])%(mod-1);
}
ans=1;
fo(i,1,len)
{
for (s=0,j=p[i]; j<=n; j*=p[i]) s=(s+js(j))%(mod-1);
ans=ans*qpower(p[i],(s+mod-1)%(mod-1))%mod;
}
printf("%lld
",ans);
fclose(stdin);
fclose(stdout);
return 0;
}