一、题目
二、解法
求无向联通图的个数是在是太难做了,我们考虑从无向图个数方面入手。
设
g
(
i
)
g(i)
g(i)表示无向图点数为
i
i
i的总数,考虑每一条边的选与不选,方案数是
2
C
(
n
,
2
)
2^{C(n,2)}
2C(n,2),设
f
(
i
)
f(i)
f(i)为无向连通图的点数为
i
i
i的方案数,那么我们可以用
f
f
f来算
g
g
g,枚举一边联通,另外一边随便选:
g
(
i
)
=
∑
j
=
1
i
C
i
−
1
j
−
1
⋅
f
(
j
)
⋅
g
(
i
−
j
)
g(i)=\sum_{j=1}^i C_{i-1}^{j-1}\cdot f(j)\cdot g(i-j)
g(i)=j=1∑iCi−1j−1⋅f(j)⋅g(i−j)为什么这里是
C
n
−
1
i
−
1
C_{n-1}^{i-1}
Cn−1i−1呢,因为我们要固定
1
1
1号点作为联通图的一个点,这样就可以保证相同形状的图只会被算一次。很容易看出来上面的柿子像卷积,再加上题目给的模数提示了这是
NTT
\text{NTT}
NTT的标准模数,我们考虑把组合拆开,重写这个柿子:
g
(
i
)
(
i
−
1
)
!
=
∑
j
=
1
i
f
(
j
)
(
j
−
1
)
!
⋅
g
(
i
−
j
)
(
i
−
j
)
!
\frac{g(i)}{(i-1)!}=\sum_{j=1}^i\frac{f(j)}{(j-1)!}\cdot\frac{g(i-j)}{(i-j)!}
(i−1)!g(i)=j=1∑i(j−1)!f(j)⋅(i−j)!g(i−j)令
G
(
i
)
=
g
(
i
)
(
i
−
1
)
!
G(i)=\frac{g(i)}{(i-1)!}
G(i)=(i−1)!g(i),
H
(
i
)
=
g
(
i
)
i
!
H(i)=\frac{g(i)}{i!}
H(i)=i!g(i),
F
(
i
)
=
f
(
i
)
(
i
−
1
)
!
F(i)=\frac{f(i)}{(i-1)!}
F(i)=(i−1)!f(i),那么
F
=
G
×
H
−
1
F=G\times H^{-1}
F=G×H−1,这里需要用到多项式求逆。
#include <cstdio>
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 800005;
const int MOD = 1004535809;
int read()
{
int num=0,flag=1;
char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,len,lg,Rev[MAXN],fac[MAXN],inv[MAXN];
int A[MAXN],B[MAXN],C[MAXN],F[MAXN],G[MAXN];
void init(int n)
{
fac[0]=inv[0]=inv[1]=1;
for(int i=2; i<=n; i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1; i<=n; i++) inv[i]=inv[i]*inv[i-1]%MOD;
for(int i=1; i<=n; i++) fac[i]=fac[i-1]*i%MOD;
}
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void NTT(int *a,int len,int tmp)
{
lg=(int)round(log2(len));
for(int i=0; i<len; i++)
{
Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
if(i<Rev[i])
swap(a[i],a[Rev[i]]);
}
for(int s=2; s<=len; s<<=1)
{
int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s):qkpow(3,(MOD-1)-(MOD-1)/s);
for(int i=0; i<len; i+=s)
{
int x=1;
for(int j=0; j<t; j++,x=x*w%MOD)
{
int fe=a[i+j],fo=a[i+j+t];
a[i+j]=(fe+x*fo)%MOD;
a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
}
}
}
if(tmp==1) return ;
int inv=qkpow(len,MOD-2);
for(int i=0; i<len; i++)
a[i]=a[i]*inv%MOD;
}
void work(int n,int *a,int *b)
{
len=1;
while(len<=2*n) len<<=1;
for(int i=0; i<len; i++) A[i]=B[i]=0;
for(int i=0; i<n; i++) A[i]=a[i];
for(int i=0; i<n; i++) B[i]=b[i];
NTT(A,len,1);
NTT(B,len,1);
for(int i=0; i<len; i++) A[i]=2*B[i]-B[i]*B[i]%MOD*A[i],A[i]%=MOD;
NTT(A,len,-1);
for(int i=0; i<n; i++) b[i]=(A[i]%MOD+MOD)%MOD;
}
void get(int n,int *a,int *b)
{
int cur=1;
for(int i=0; i<n; i++) b[i]=0;
b[0]=qkpow(a[0],MOD-2);
while(cur<n)
{
cur<<=1;
work(cur,a,b);
}
}
signed main()
{
n=read();
init(n);
F[0]=1;
for(int i=1; i<=n; i++)
{
int tmp=qkpow(2,i*(i-1)/2%(MOD-1));
G[i]=tmp*inv[i-1]%MOD;
F[i]=tmp*inv[i]%MOD;
}
get(n+1,F,C);
len=1;
while(len<=2*n+2) len<<=1;
NTT(G,len,1);
NTT(C,len,1);
for(int i=0; i<len; i++) G[i]=G[i]*C[i]%MOD;
NTT(G,len,-1);
printf("%lld\n",G[n]*fac[n-1]%MOD);
}