题目大意
%
给定
n
n
n,求有
n
n
n 个点的简单(无重边无自环)无向连通图的数目。
输出模
479
∗
2
21
+
1
=
1004535809
479*2^{21}+1=1004535809
479∗221+1=1004535809 意义下的答案。
数据范围
1
⩽
n
⩽
130000
1\leqslant n\leqslant 130000
1⩽n⩽130000
题解
%
题中所给模数的原根为3。
定义
f
n
f_n
fn 为答案,即简单无向连通图的数量,
g
n
g_n
gn 为无向图的数量,显然有
g
0
=
g
1
=
1
g_0=g_1=1
g0=g1=1。可以发现,对于任意
n
⩾
2
n\geqslant2
n⩾2,图中最多有
C
n
2
C_n^2
Cn2 条无向边,每条边可有可无,因而有
g
n
=
2
C
n
2
(1)
g_n=2^{C_n^2}\tag 1
gn=2Cn2(1)
%
若其中一个点所在的连通图大小为
i
i
i,则
g
n
=
∑
i
=
1
n
C
n
−
1
i
−
1
f
i
g
n
−
i
(2)
g_n=\sum_{i=1}^nC_{n-1}^{i-1}f_ig_{n-i}\tag{2}
gn=i=1∑nCn−1i−1fign−i(2)
%
把求和拆一下,得
g
n
=
f
n
+
∑
i
=
1
n
−
1
C
n
−
1
i
−
1
f
i
g
n
−
i
g_n=f_n+\sum_{i=1}^{n-1}C_{n-1}^{i-1}f_ig_{n-i}
gn=fn+i=1∑n−1Cn−1i−1fign−i
%
移项,组合数展开
f
n
=
g
n
−
∑
i
=
1
n
−
1
(
n
−
1
)
!
(
i
−
1
)
!
(
n
−
i
)
!
f
i
g
n
−
i
=
g
n
−
(
n
−
1
)
!
∑
i
=
1
n
−
1
f
i
(
i
−
1
)
!
g
n
−
i
(
n
−
i
)
!
\begin{aligned} f_n&=g_n-\sum_{i=1}^{n-1}\frac{(n-1)!}{(i-1)!(n-i)!}f_ig_{n-i}\\ &=g_n-(n-1)!\sum_{i=1}^{n-1}\frac{f_i}{(i-1)!}\frac{g_{n-i}}{(n-i)!}\\ \end{aligned}
fn=gn−i=1∑n−1(i−1)!(n−i)!(n−1)!fign−i=gn−(n−1)!i=1∑n−1(i−1)!fi(n−i)!gn−i
%
考虑
a
i
=
f
i
(
i
−
1
)
!
,
b
i
=
g
i
i
!
\begin{aligned} a_i=\frac {f_i}{(i-1)!},b_i=\frac{g_{i}}{i!} \end{aligned}
ai=(i−1)!fi,bi=i!gi
%
因而
f
n
=
(
n
−
1
)
!
a
n
,
g
n
=
i
!
b
i
f_n=(n-1)!a_n,g_n=i!b_i
fn=(n−1)!an,gn=i!bi,则有
(
n
−
1
)
!
a
n
=
n
!
b
n
−
(
n
−
1
)
!
∑
i
=
1
n
−
1
a
i
b
n
−
i
(n-1)!a_n=n!b_n-(n-1)!\sum_{i=1}^{n-1}a_ib_{n-i}
(n−1)!an=n!bn−(n−1)!i=1∑n−1aibn−i
%
同除
(
n
−
1
)
!
(n-1)!
(n−1)!,得
a
n
=
n
b
n
−
∑
i
=
1
n
−
1
a
i
b
n
−
i
a_n=nb_n-\sum_{i=1}^{n-1}a_ib_{n-i}
an=nbn−i=1∑n−1aibn−i
% 至此便可以用分治FFT处理了,时间复杂度 Θ ( n log 2 2 n ) \Theta(n\log_2^2 n) Θ(nlog22n)。
%
我们继续考虑
(
2
)
(2)
(2) 式,将
(
1
)
(1)
(1) 式代入
(
2
)
(2)
(2) 式,展开,得
2
C
n
2
=
∑
i
=
1
n
(
n
−
1
)
!
(
i
−
1
)
!
(
n
−
i
)
!
f
i
2
C
n
−
i
2
2^{C_n^2}=\sum_{i=1}^n\frac{(n-1)!}{(i-1)!(n-i)!}f_i2^{C_{n-i}^2}
2Cn2=i=1∑n(i−1)!(n−i)!(n−1)!fi2Cn−i2
%
同除
(
n
−
1
)
!
(n-1)!
(n−1)!,得
2
C
n
2
(
n
−
1
)
!
=
∑
i
=
1
n
f
i
(
i
−
1
)
!
2
C
n
−
i
2
(
n
−
i
)
!
\frac{2^{C_n^2}}{(n-1)!}=\sum_{i=1}^n\frac{f_i}{(i-1)!}\frac{2^{C_{n-i}^2}}{(n-i)!}
(n−1)!2Cn2=i=1∑n(i−1)!fi(n−i)!2Cn−i2
%
可以发现这是卷积的形式,定义
F
(
x
)
=
∑
i
=
1
n
f
i
(
i
−
1
)
!
x
i
G
(
x
)
=
∑
i
=
1
n
2
C
i
2
i
!
x
i
H
(
x
)
=
∑
i
=
1
n
2
C
i
2
(
i
−
1
)
!
x
i
\begin{aligned} F(x)&=\sum_{i=1}^n\frac{f_i}{(i-1)!}x^i\\ G(x)&=\sum_{i=1}^n\frac{2^{C_{i}^2}}{i!}x^i\\ H(x)&=\sum_{i=1}^n\frac{2^{C_i^2}}{(i-1)!}x^i \end{aligned}
F(x)G(x)H(x)=i=1∑n(i−1)!fixi=i=1∑ni!2Ci2xi=i=1∑n(i−1)!2Ci2xi
% 则有 F ∗ G = H F*G=H F∗G=H,因而有 F = H ∗ G − 1 F=H*G^{-1} F=H∗G−1,多项式求逆即可求出 F F F,再对答案乘 ( n − 1 ) ! (n-1)! (n−1)! 即可。时间复杂度为 Θ ( n log 2 n ) \Theta(n\log_2 n) Θ(nlog2n)。
代码
分治FFT?不存在的。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define MAXN 460010
const long long mod=1004535809,G=3,inG=334845270;
inline long long pows(long long a,long long b){
long long ret=1;
while(b){
if(b&1) ret=ret*a%mod;
b>>=1;a=a*a%mod;
} return ret;
}
#define invt(x) pows(x,mod-2)
class poly{
public:
static const int maxn=MAXN;
long long *t;
int limit,l,inmit;
inline long long& operator[](int x){return t[x];}
inline const long long& operator[](int x)const{return t[x];}
inline poly(int n=0):t(NULL),limit(1),l(0){init(n);}
inline void init(int n){
if(limit>=n) return;
if(t) free(t);
while(limit<n) limit<<=1,++l;
inmit=invt(limit);
t=(long long*)calloc(limit+5,sizeof(long long));
}
void fnt(int type){
static long long r[MAXN];
static int now=0;
if(now!=limit&&(now=limit))
for(int i=1;i<limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
for(int i=1;i<limit;i++)
if(i<r[i]) swap(t[i],t[r[i]]);
for(int mid=1;mid<limit;mid<<=1){
long long wn=pows(type==1?G:inG,(mod-1)/(mid<<1));
for(int j=0,B=(mid<<1);j<limit;j+=B){
long long w=1;
for(int k=0;k<mid;k++,w=w*wn%mod){
int x=t[j+k],y=w*t[j+k+mid]%mod;
t[j+k]=(x+y)%mod;t[j+k+mid]=(x-y+mod)%mod;
}
}
} if(type==-1) for(int i=0;i<limit;i++)
t[i]=t[i]*inmit%mod;
}
inline void inv(int n,poly &g)const{
g.init(n+n);
if(n==1) return(void)(g[0]=invt(t[0]));
inv(n+1>>1,g);
poly F(n+n),G(n+n);
for(int i=0;i<n;i++) F[i]=t[i];
for(int i=0;i<n+1>>1;i++) G[i]=g[i];
F.fnt(1);G.fnt(1);
for(int i=0;i<F.limit;i++)
F[i]=G[i]*((2-F[i]*G[i]%mod+mod)%mod)%mod;
F.fnt(-1);
for(int i=0;i<n;i++) g[i]=F[i];
}
};
int n;
long long frac[MAXN],ifrac[MAXN];
int main(){
scanf("%d",&n);++n;
frac[0]=1;
for(int i=1;i<=n;i++)
frac[i]=frac[i-1]*i%mod;
ifrac[n]=invt(frac[n]);
for(int i=n-1;i>=0;i--)
ifrac[i]=ifrac[i+1]*(i+1)%mod;
poly G(n+n),H(n+n),inv_G(n+n);
for(int i=1;i<n;i++)
G[i]=ifrac[i]*pows(2,((long long)i*(i-1)/2))%mod;
for(int i=1;i<n;i++)
H[i]=ifrac[i-1]*pows(2,((long long)i*(i-1)/2))%mod;
G[0]=1;G.inv(n,inv_G);
inv_G.fnt(1);H.fnt(1);
for(int i=0;i<H.limit;i++)
H[i]=inv_G[i]*H[i]%mod;
H.fnt(-1);
printf("%lld\n",H[n-1]*frac[n-2]%mod);
return 0;
}