题目描述
题目大意:求n个点的无重边无自环无向连通图数目。
题解
这题好强啊。。
设
f(i)
表示与1连通的连通块大小为i(包括1)的连通图数目
如果要是将i个点之间的
2i(i−1)2
条边随便连的话会有一些不合法的方案,即有一些点和i不连通,所以这里要容斥一下
f(i)=2i(i−1)2−∑j=0i−1Cj−1i−1⋅f(j)⋅2(i−j)(i−j−1)2
这个式子的意义是先随便连边,然后除去不合法方案;对于不合法方案,先从剩余的i-1个点里选出j-1个点,然后强制选出来的这j个点和1连通,剩余的点和1不连通,它们之间随便连边
然后移项之后将组合数展开化简
f(i)=2i(i−1)2−∑j=0i−1Cj−1i−1⋅f(j)⋅2(i−j)(i−j−1)2
∑j=0iCj−1i−1⋅f(j)⋅2(i−j)(i−j−1)2=2i(i−1)2
∑j=0i(i−1)!(j−1)!(i−j)!⋅f(j)⋅2(i−j)(i−j−1)2=2i(i−1)2
∑j=0if(j)(j−1)!⋅2(i−j)(i−j−1)2(i−j)!=2i(i−1)2(i−1)!
令 A(i)=f(i)(i−1)!,B(i)=2i(i−1)2i!,C(i)=2i(i−1)2(i−1)!
那么就可以将这个式子化成一个卷积的形式了
C(i)=∑j=0iA(j)B(i−j)
现在我们可以预处理出来 B 和
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define LL long long
#define Mod 1004535809
#define N 600005
int lim,m,n,L,R[N];
LL mul[N],inv[N],mulinv[N];
LL A[N],B[N],C[N],invB[N],c[N];
void init()
{
mul[0]=1LL;
for (int i=1;i<=lim;++i) mul[i]=mul[i-1]*(LL)i%Mod;
inv[1]=1LL;
for (int i=2;i<=n<<1;++i) inv[i]=inv[Mod%i]*(Mod-Mod/i)%Mod;
mulinv[0]=1LL;
for (int i=1;i<=lim;++i) mulinv[i]=mulinv[i-1]*inv[i]%Mod;
}
LL fast_pow(LL a,LL p)
{
LL ans=1LL;
for (;p;p>>=1LL,a=a*a%Mod)
if (p&1LL)
ans=ans*a%Mod;
return ans;
}
void NTT(LL a[N],int n,int opt)
{
L=0;for (int i=1;i<n;i<<=1) ++L;
for (int i=0;i<n;++i)
R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
for (int i=0;i<n;++i)
if (i<R[i]) swap(a[i],a[R[i]]);
for (int k=1;k<n;k<<=1)
{
LL wn=fast_pow(3,(Mod-1)/(k<<1));
for (int i=0;i<n;i+=(k<<1))
{
LL w=1LL;
for (int j=0;j<k;++j,w=w*wn%Mod)
{
LL x=a[i+j],y=w*a[i+j+k]%Mod;
a[i+j]=(x+y)%Mod;a[i+j+k]=(x-y+Mod)%Mod;
}
}
}
if (opt==-1) reverse(a+1,a+n);
}
void inverse(int n,LL a[N],LL b[N],LL c[N])
{
if (n==1) b[0]=fast_pow(a[0],Mod-2);
else
{
inverse(n>>1,a,b,c);
int k=n<<1;
for (int i=0;i<n;++i) c[i]=a[i];
for (int i=n;i<k;++i) c[i]=0;
NTT(c,k,1);NTT(b,k,1);
for (int i=0;i<k;++i) b[i]=(2-c[i]*b[i]%Mod+Mod)%Mod*b[i]%Mod;
NTT(b,k,-1);
for (int i=0;i<n;++i) b[i]=b[i]*inv[k];
for (int i=n;i<k;++i) b[i]=0;
}
}
int main()
{
scanf("%d",&lim);
m=lim<<1;
for (n=1;n<=m;n<<=1);
init();
B[0]=1LL;C[0]=0LL;
for (int i=1;i<=lim;++i)
{
LL mi=fast_pow(2,(LL)i*(i-1)/2);
B[i]=mi*mulinv[i]%Mod;C[i]=mi*mulinv[i-1]%Mod;
}
inverse(n,B,invB,c);
NTT(invB,n,1);NTT(C,n,1);
for (int i=0;i<=n;++i) A[i]=invB[i]*C[i]%Mod;
NTT(A,n,-1);
for (int i=0;i<=n;++i) A[i]=A[i]*inv[n]%Mod;
printf("%lld\n",A[lim]*mul[lim-1]%Mod);
}