Description
刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n 个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通.
为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n 个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^21 + 1)即可.
n<=130000
Solution
我们有两种思路
一是考虑DP
设
f
[
i
]
f[i]
f[i]为i个点的无向连通图个数,
g
[
i
]
g[i]
g[i]为i个点的无向图个数
显然
g
[
i
]
=
2
n
(
n
−
1
)
2
g[i]=2^{n(n-1)\over 2}
g[i]=22n(n−1)(每条边有或没有)
正难则反,我们可以考虑用总的减去不连通的
不妨枚举某一个点所在的联通块的大小(为了避免重复,我们当做枚举的点是标号最小的那个)
那么
f
[
i
]
=
g
[
i
]
−
∑
j
=
1
i
−
1
f
[
j
]
∗
(
i
−
1
j
−
1
)
∗
g
[
i
−
j
]
f[i]=g[i]-\sum\limits_{j=1}^{i-1}f[j]*{i-1\choose j-1}*g[i-j]
f[i]=g[i]−j=1∑i−1f[j]∗(j−1i−1)∗g[i−j]
枚举1号点所在连通块大小,组合数计算其中点的情况,剩下i-j个点随便组成一个无向图
这一定唯一对应了一种不合法情况
直接转移复杂度是不能接受的,考虑优化
看到
f
[
j
]
∗
g
[
i
−
j
]
f[j]*g[i-j]
f[j]∗g[i−j]我们容易想到卷积
组合数拆开
f
[
i
]
=
g
[
i
]
−
∑
j
=
1
i
−
1
f
[
j
]
∗
(
i
−
1
)
!
(
j
−
1
)
!
(
i
−
j
)
!
∗
g
[
i
−
j
]
f[i]=g[i]-\sum\limits_{j=1}^{i-1}f[j]*{(i-1)!\over (j-1)!(i-j)!}*g[i-j]
f[i]=g[i]−j=1∑i−1f[j]∗(j−1)!(i−j)!(i−1)!∗g[i−j]
分配一下,(i-1)除过来
f
[
i
]
(
i
−
1
)
!
=
g
[
i
]
(
i
−
1
)
!
−
∑
j
=
1
i
−
1
f
[
j
]
(
j
−
1
)
!
∗
g
[
i
−
j
]
(
i
−
j
)
!
{f[i]\over (i-1)!}={g[i]\over (i-1)!}-\sum\limits_{j=1}^{i-1}{f[j]\over (j-1)!}*{g[i-j]\over (i-j)!}
(i−1)!f[i]=(i−1)!g[i]−j=1∑i−1(j−1)!f[j]∗(i−j)!g[i−j]
此时可以考虑用CDQ分治
对于分治区间
[
l
,
r
]
[l,r]
[l,r],递归计算
[
l
,
m
i
d
]
[l,mid]
[l,mid],然后用NTT快速计算出
[
l
,
m
i
d
]
[l,mid]
[l,mid]对
[
m
i
d
+
1
,
r
]
[mid+1,r]
[mid+1,r]的贡献,然后递归右边即可
复杂度 O ( n log 2 n ) O(n\log^2 n) O(nlog2n)
然而这不是重点
如果我们移项,将左边的并入右边卷积式子,就有
g
[
i
]
(
i
−
1
)
!
=
∑
j
=
1
i
f
[
j
]
(
j
−
1
)
!
∗
g
[
i
−
j
]
(
i
−
j
)
!
{g[i]\over (i-1)!}=\sum\limits_{j=1}^{i}{f[j]\over (j-1)!}*{g[i-j]\over (i-j)!}
(i−1)!g[i]=j=1∑i(j−1)!f[j]∗(i−j)!g[i−j]
如果我们将上面三部分分别看成三个多项式
A
(
x
)
,
F
(
x
)
,
B
(
x
)
A(x),F(x),B(x)
A(x),F(x),B(x)
A
(
x
)
=
∑
i
>
0
g
[
i
]
∗
x
i
(
i
−
1
)
!
A(x)=\sum\limits_{i>0}{g[i]*x^i\over (i-1)!}
A(x)=i>0∑(i−1)!g[i]∗xi
B
(
x
)
=
∑
i
≥
0
g
[
i
]
∗
x
i
i
!
B(x)=\sum\limits_{i\geq0}{g[i]*x^i\over i!}
B(x)=i≥0∑i!g[i]∗xi
F
(
x
)
=
∑
i
>
0
f
[
i
]
∗
x
i
(
i
−
1
)
!
F(x)=\sum\limits_{i>0}{f[i]*x^i\over (i-1)!}
F(x)=i>0∑(i−1)!f[i]∗xi
容易看出
A
(
x
)
=
B
(
x
)
F
(
x
)
A(x)=B(x)F(x)
A(x)=B(x)F(x)
由于
A
(
x
)
,
B
(
x
)
,
F
(
x
)
A(x),B(x),F(x)
A(x),B(x),F(x)都是无穷多项的多项式(形式幂级数),我们只需要
x
n
x^n
xn前面的项
用多项式求逆,可以得到
F
(
x
)
=
A
(
x
)
B
−
1
(
x
)
F(x)=A(x)B^{-1}(x)
F(x)=A(x)B−1(x)
这样在
O
(
n
log
n
)
O(n\log n)
O(nlogn)时间内就得到答案了
看出来了吗? A ( x ) , B ( x ) , F ( x ) A(x),B(x),F(x) A(x),B(x),F(x)实际上都是指数型生成函数
从生成函数的角度入手
设
f
[
i
]
f[i]
f[i]的EGF为
F
′
(
x
)
F'(x)
F′(x),
g
[
i
]
g[i]
g[i]的EGF为
G
′
(
x
)
G'(x)
G′(x)
那么
F
′
(
x
)
=
∑
i
>
0
f
[
i
]
∗
x
i
i
!
F'(x)=\sum\limits_{i>0}{f[i]*x^i\over i!}
F′(x)=i>0∑i!f[i]∗xi
A
′
(
x
)
=
∑
i
>
0
g
[
i
]
∗
x
i
i
!
A'(x)=\sum\limits_{i>0}{g[i]*x^i\over i!}
A′(x)=i>0∑i!g[i]∗xi
无向图实际上就是由若干个连通块拼接而成,因此这实际上就是带标号对象重复拼接的过程(若干个F’卷在一起)。
因此
A
′
(
x
)
=
∑
i
>
0
(
F
′
(
x
)
)
i
i
!
A'(x)=\sum\limits_{i>0}{\left(F'(x)\right)^i\over i!}
A′(x)=i>0∑i!(F′(x))i(因为连通块之间没有顺序之分,因此要除以i!)
也就是说
A
′
(
x
)
=
e
F
′
(
x
)
A'(x)=e^{F'(x)}
A′(x)=eF′(x)
那么应用多项式求
ln
\ln
ln就可以通过A’来求出F’了,进而求出f
复杂度同样是 O ( n log n ) O(n\log n) O(nlogn)
Code
以下是多项式求逆的方法
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 130005
#define M 524288
#define LL long long
#define mo 1004535809
using namespace std;
int bit[M+1],n,wi[M+1],wg[M+1],cf[20],l2[M+1];
LL a[M+1],b[M+1],u1[M+1],u2[M+1],c[M+1],ny[M+1];
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}
void prp(int num)
{
int L=l2[num];
fo(i,0,num-1) bit[i]=(bit[i>>1]>>1)|((i&1)<<(L-1));
fo(i,0,num) wi[i]=wg[i*(M/num)];
}
void NTT(LL *a,bool pd,int num)
{
fo(i,0,num-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
int lim=num>>1;
LL v=0;
for(int m=2,half=1;m<=num;half=m,m<<=1,lim>>=1)
{
fo(i,0,half-1)
{
LL w=(!pd)?wi[i*lim]:wi[num-i*lim];
for(int j=i;j<num;j+=m)
{
v=w*a[j+half]%mo;
a[j+half]=(a[j]-v+mo)%mo;
a[j]=(a[j]+v)%mo;
}
}
}
if(pd) fo(i,0,num-1) a[i]=a[i]*ny[num]%mo;
}
void mul(const LL *a,const LL *b,LL *c,int L)
{
int num=cf[L];
prp(num);
fo(i,0,num-1) u1[i]=a[i],c[i]=b[i];
NTT(u1,0,num),NTT(c,0,num);
fo(i,0,num-1) c[i]=c[i]*u1[i]%mo;
NTT(c,1,num);
}
void make(const LL *a,LL *b,int L)
{
b[0]=ksm(a[0],mo-2);
for(int m=1,t=2,num=4;m<cf[L];m=t,t=num,num<<=1)
{
prp(num);
fo(i,0,m-1) u1[i]=a[i],u2[i]=b[i];
fo(i,m,t-1) u1[i]=a[i],u2[i]=0;
fo(i,t,num-1) u1[i]=u2[i]=0;
NTT(u1,0,num),NTT(u2,0,num);
fo(i,0,num-1) u2[i]=u2[i]*u2[i]%mo*u1[i]%mo;
NTT(u2,1,num);
fo(i,0,t-1) b[i]=((LL)2*b[i]-u2[i]+mo)%mo;
fo(i,t,num-1) b[i]=0;
}
}
int main()
{
cin>>n;
cf[0]=1;
fo(i,1,19) l2[cf[i]=cf[i-1]*2]=i;
fod(i,M-1,2) if(!l2[i]) l2[i]=l2[i+1];
ny[0]=ny[1]=1;
fo(i,2,M) ny[i]=(-ny[mo%i]*(LL)(mo/i)%mo+mo)%mo;
LL c1=ksm(3,1916);
wg[0]=1;
fo(i,1,M) wg[i]=wg[i-1]*c1%mo;
LL s=1,t=1;
b[0]=1;
fo(i,1,n)
{
s=s*ksm(i,mo-2)%mo;
LL v=ksm(2,(LL)i*(LL)(i-1)/2)*s%mo;
a[i]=v*(LL)i%mo;
b[i]=v%mo;
if(i!=n) t=t*(LL)i%mo;
}
make(b,c,l2[n+1]);
mul(a,c,b,l2[n+1]);
printf("%lld",t*b[n]%mo);
}