BZOJ4714 : 旋转排列
对于每个$k$,问题等价于求有多少置换满足:
1.存在一个循环长度为$k$
2.任意一个循环长度$\geq 2$
枚举这种环的个数$t$:
设$g_t$表示至少有$kt$个人分成$t$个长度为$k$的循环的方案数,考虑枚举第一个人和哪些人分在了一起,同时有$(k-1)!$种可能的环,有$g_t=C(kt-1,k-1)g_{t-1}(k-1)!$。
设$d_i$表示$i$个人错位排列的方案数,那么至少有$t$个长度为$k$的循环的方案数为$C(n,kt)g_td_{n-kt}$。
考虑容斥,则这部分对答案的贡献为$(-1)^{t-1}C(n,kt)g_td_{n-kt}$。
时间复杂度$O(n\log n)$。
#include<cstdio> const int N=500010,P=1000000007; int n,i,j,k,t,f[N],inv[N],d[N],g[N],ans; int main(){ scanf("%d",&n); for(f[0]=i=1;i<=n;i++)f[i]=1LL*f[i-1]*i%P; for(inv[0]=inv[1]=1,i=2;i<=n;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P; for(i=1;i<=n;i++)inv[i]=1LL*inv[i-1]*inv[i]%P; for(d[0]=d[2]=1,i=3;i<=n;i++)d[i]=1LL*(i-1)*(d[i-2]+d[i-1])%P; for(i=0;i<=n;i++)d[i]=1LL*d[i]*inv[i]%P; for(g[0]=1,i=2;i<=n;i++)for(j=1,k=i;k<=n;j++,k+=i){ g[j]=1LL*g[j-1]*f[k-1]%P*inv[k-i]%P; t=1LL*inv[k]*d[n-k]%P*g[j]%P; if(j&1){ ans+=t; if(ans>=P)ans-=P; }else{ ans-=t; if(ans<0)ans+=P; } } ans=1LL*ans*f[n]%P; return printf("%d",ans),0; }