Description
刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^ 21 + 1)即可.
方案数 mod 1004535809.
对于 100%的数据, n <= 130000
Solution
之前似乎做过求给定连通块数量的dp,这个也是类似吧
令f[i]表示i个点形成连通块的方案数,有
f
[
i
]
=
2
C
(
i
,
2
)
−
∑
j
=
1
i
−
1
C
(
i
−
1
,
j
−
1
)
×
f
[
j
]
×
2
C
(
i
−
j
,
2
)
f[i]=2^{C(i,2)}-\sum_{j=1}^{i-1}C(i-1,j-1)\times f[j]\times 2^{C(i-j,2)}
f[i]=2C(i,2)−∑j=1i−1C(i−1,j−1)×f[j]×2C(i−j,2)
大概就是容斥,总的方案数减去不符合的方案数,j用来枚举点1所在连通块的大小,剩下的随便连
我们令
w
n
=
2
C
(
n
,
2
)
w_n=2^{C(n,2)}
wn=2C(n,2),拆一下柿子可以发现
f
[
i
]
=
w
i
−
∑
j
=
1
i
−
1
f
[
j
]
×
(
i
−
1
)
!
(
j
−
1
)
!
×
w
i
−
j
(
i
−
j
)
!
f[i]=w_i-\sum_{j=1}^{i-1}\frac{f[j]\times(i-1)!}{(j-1)!}\times \frac{w_{i-j}}{(i-j)!}
f[i]=wi−∑j=1i−1(j−1)!f[j]×(i−1)!×(i−j)!wi−j
两边同时除(i-1)!得到
f
[
i
]
(
i
−
1
)
!
=
w
i
(
i
−
1
)
!
−
∑
j
=
1
i
−
1
f
[
j
]
(
j
−
1
)
!
×
w
i
−
j
(
i
−
j
)
!
\frac{f[i]}{(i-1)!}=\frac{w_i}{(i-1)!}-\sum_{j=1}^{i-1}\frac{f[j]}{(j-1)!}\times\frac{w_{i-j}}{(i-j)!}
(i−1)!f[i]=(i−1)!wi−∑j=1i−1(j−1)!f[j]×(i−j)!wi−j
移项得到
w
i
(
i
−
1
)
!
=
∑
j
=
1
i
f
[
j
]
(
j
−
1
)
!
×
w
i
−
j
(
i
−
j
)
!
\frac{w_i}{(i-1)!}=\sum_{j=1}^{i}\frac{f[j]}{(j-1)!}\times \frac{w_{i-j}}{(i-j)!}
(i−1)!wi=∑j=1i(j−1)!f[j]×(i−j)!wi−j
分别令三坨东西为三个多项式A B C,得到
A
=
B
∗
C
⇔
B
=
A
∗
C
−
1
A=B*C\Leftrightarrow B=A*C^{-1}
A=B∗C⇔B=A∗C−1,求出多项式C(x)的逆元即可
多项式求逆:
假定我们已经求出了多项式
A
(
x
)
A(x)
A(x)在模
x
n
2
x^{\frac{n}{2}}
x2n意义下的逆元多项式
B
(
x
)
B(x)
B(x),现在要求多项式
A
(
x
)
A(x)
A(x)在模
x
n
x^{n}
xn意义下的逆元多项式
G
(
x
)
G(x)
G(x)
根据定义可得
A
(
x
)
∗
B
(
x
)
≡
1
(
m
o
d
x
n
2
)
A(x)*B(x)\equiv 1\pmod {x^{\frac{n}{2}}}
A(x)∗B(x)≡1(modx2n)
A
(
x
)
∗
G
(
x
)
≡
1
(
m
o
d
x
n
)
A(x)*G(x)\equiv 1\pmod {x^n}
A(x)∗G(x)≡1(modxn)
那么两式相减(考虑同余的性质,可以撕烤一下为什么是这样
B
(
x
)
−
G
(
x
)
≡
0
(
m
o
d
x
n
2
)
B(x)-G(x)\equiv 0\pmod {x^{\frac{n}{2}}}
B(x)−G(x)≡0(modx2n)
平方得到
B
2
(
x
)
+
G
2
(
x
)
−
2
B
(
x
)
G
(
x
)
≡
0
(
m
o
d
x
n
)
B^2(x)+G^2(x)-2B(x)G(x)\equiv 0\pmod {x^n}
B2(x)+G2(x)−2B(x)G(x)≡0(modxn)
左右同时乘
A
(
x
)
A(x)
A(x)得到
G
(
x
)
≡
2
B
(
x
)
−
A
B
2
(
x
)
(
m
o
d
x
n
)
G(x)\equiv 2B(x)-AB^2(x) \pmod{x^n}
G(x)≡2B(x)−AB2(x)(modxn) 注意这里全部变成
x
n
x^n
xn同余下了,只能消去此时的
G
(
x
)
G(x)
G(x)
这样就能用递归(or倍增 的方法搞定多项式求逆了,注意把多项式补齐为2的次幂项
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
typedef long long LL;
const int MOD=1004535809;
const int N=300005;
const int g=3;
LL rev[N],tmp[N],fac[N],ny[N],a[N],c[N],nc[N];
LL nyn;
LL ksm(LL x,LL dep) {
LL ret=1;
while (dep) {
if (dep&1) ret=ret*x%MOD;
x=x*x%MOD; dep/=2;
}
return ret;
}
void NTT(LL *a,int len,int f) {
int lg=log(len)/log(2)+0.1;
for (int i=0;i<len;i++) {
rev[i]=(rev[i/2]/2)|((i&1)<<(lg-1));
if (i<rev[i]) std:: swap(a[i],a[rev[i]]);
}
for (int i=1;i<len;i*=2) {
LL wn=ksm(g,(MOD-1)/i/2);
if (f==-1) wn=ksm(g,(MOD-1)-(MOD-1)/i/2);
for (int j=0;j<len;j+=i*2) {
LL w=1;
for (int k=0;k<i;k++) {
int u=a[j+k],v=a[j+i+k]*w%MOD;
a[j+k]=(u+v)%MOD; a[j+i+k]=(u-v+MOD)%MOD;
w=wn*w%MOD;
}
}
}
if (f==-1) {
LL nnyy=ksm(len,MOD-2);
for (int i=0;i<len;i++) a[i]=a[i]*nnyy%MOD;
}
}
void get_ny(LL *a,LL *b,int n) {
if (n==1) {
b[0]=ksm(a[0],MOD-2);
return ;
}
get_ny(a,b,n/2);
memcpy(tmp,a,sizeof(a[0])*n);
memset(tmp+n,0,sizeof(tmp[0])*n);
NTT(tmp,n*2,1); NTT(b,n*2,1);
for (int i=0;i<n*2;i++) tmp[i]=((LL)b[i]%MOD*(2-tmp[i]*b[i]%MOD+MOD)%MOD)%MOD;
NTT(tmp,n*2,-1);
for (int i=0;i<n;i++) b[i]=tmp[i];
memset(b+n,0,sizeof(b[0])*n);
}
int main(void) {
int n; scanf("%d",&n);
int len; for (len=1;len<=n;len*=2);
fac[0]=ny[0]=1;
for (int i=1;i<=n;i++) fac[i]=fac[i-1]*(LL)i%MOD,ny[i]=ksm(fac[i],MOD-2);
for (int i=0;i<=n;i++) c[i]=ksm(2,((LL)(i-1)*i/2)%(MOD-1))*ny[i]%MOD;
for (int i=1;i<=n;i++) a[i]=ksm(2,((LL)(i-1)*i/2)%(MOD-1))*ny[i-1]%MOD;
get_ny(c,nc,len);
NTT(a,len*2,1); NTT(nc,len*2,1);
for (int i=0;i<len*2;i++) a[i]=a[i]*nc[i]%MOD;
NTT(a,len*2,-1);
printf("%lld\n", a[n]*fac[n-1]%MOD);
return 0;
}