题解:
考试时脑子不清醒啊,搞了很久……其实是个普通的容斥。问题可以转化为,若每个点ii都向连边,一种方案的贡献就是这个图中不同长度的环的个数。那么我们可以枚举环的长度ii,然后再算至少有个环的方案,容斥即可。具体的算法如下:比如至少有33个长度为的环的方案数,一共有nn个点,可以这样算:,fac[i]fac[i]表示i!i!,f[i]f[i]表示ii个数错排的方案数,其中前三个组合数的乘积表示选点的方案数,中间的阶乘的乘方是每个环中不同方法的方案数,因为是环,要固定一个起点,所以是点数-1的阶乘,错排是剩下的点合法方案的数量,最后除的阶乘是因为不考虑环与环之间的顺序。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=500010;
const int mod=1000000007;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,f[Maxn],fac[Maxn],fin[Maxn],inv[Maxn];
void pre()
{
fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=(LL)fac[i-1]*i%mod;
inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod;
fin[0]=1;
for(int i=1;i<=n;i++)fin[i]=(LL)fin[i-1]*inv[i]%mod;
f[0]=1,f[1]=0,f[2]=1;
for(int i=3;i<=n;i++)f[i]=(LL)(i-1)*((f[i-1]+f[i-2])%mod)%mod;
}
int C(int n,int m)
{
if(n<m)return 0;
return (LL)fac[n]*fin[m]%mod*fin[n-m]%mod;
}
int main()
{
n=read();
pre();
int ans=0;
for(int i=2;i<=n;i++)
{
int t=0,x=1,mx=n/i;
for(int j=1;i*j<=n;j++)
{
int rest=n-i*j;
x=(LL)x*C(n-(j-1)*i,i)%mod*fac[i-1]%mod;
if(j&1)t=(LL)(t+(LL)fin[j]*x%mod*f[rest]%mod)%mod;
else t=(LL)(t-(LL)fin[j]*x%mod*f[rest]%mod+mod)%mod;
}
ans=(ans+t)%mod;
}
printf("%d",ans);
}