Description
刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^ 21 + 1)即可.
方案数 mod 1004535809.
对于 100%的数据, n <= 130000
Solution
之前似乎做过求给定连通块数量的dp,这个也是类似吧
令f[i]表示i个点形成连通块的方案数,有f[i]=2C(i,2)−∑j=1i−1C(i−1,j−1)×f[j]×2C(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所在连通块的大小,剩下的随便连
我们令wn=2C(n,2)w_n=2^{C(n,2)}wn=2C(n,2),拆一下柿子可以发现f[i]=wi−∑j=1i−1f[j]×(i−1)!(j−1)!×wi−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)!=wi(i−1)!−∑j=1i−1f[j](j−1)!×wi−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
移项得到wi(i−1)!=∑j=1if[j](j−1)!×wi−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−1A=B*C\Leftrightarrow B=A*C^{-1}A=B∗C⇔B=A∗C−1,求出多项式C(x)的逆元即可
多项式求逆:
假定我们已经求出了多项式A(x)A(x)A(x)在模xn2x^{\frac{n}{2}}x2n意义下的逆元多项式B(x)B(x)B(x),现在要求多项式A(x)A(x)A(x)在模xnx^{n}xn意义下的逆元多项式G(x)G(x)G(x)
根据定义可得
A(x)∗B(x)≡1(modxn2)A(x)*B(x)\equiv 1\pmod {x^{\frac{n}{2}}}A(x)∗B(x)≡1(modx2n)
A(x)∗G(x)≡1(modxn)A(x)*G(x)\equiv 1\pmod {x^n}A(x)∗G(x)≡1(modxn)
那么两式相减(考虑同余的性质,可以撕烤一下为什么是这样
B(x)−G(x)≡0(modxn2)B(x)-G(x)\equiv 0\pmod {x^{\frac{n}{2}}}B(x)−G(x)≡0(modx2n)
平方得到B2(x)+G2(x)−2B(x)G(x)≡0(modxn)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)≡2B(x)−AB2(x)(modxn)G(x)\equiv 2B(x)-AB^2(x) \pmod{x^n}G(x)≡2B(x)−AB2(x)(modxn) 注意这里全部变成xnx^nxn同余下了,只能消去此时的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;
}