Description
刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n 个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通.
为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n 个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^21 + 1)即可.
n<=130000
Solution
我们有两种思路
一是考虑DP
设
为i个点的无向连通图个数,
为i个点的无向图个数
显然
(每条边有或没有)
正难则反,我们可以考虑用总的减去不连通的
不妨枚举某一个点所在的联通块的大小(为了避免重复,我们当做枚举的点是标号最小的那个)
那么
枚举1号点所在连通块大小,组合数计算其中点的情况,剩下i-j个点随便组成一个无向图
这一定唯一对应了一种不合法情况
直接转移复杂度是不能接受的,考虑优化
看到
我们容易想到卷积
组合数拆开
分配一下,(i-1)除过来
此时可以考虑用CDQ分治
对于分治区间
,递归计算
,然后用NTT快速计算出
对
的贡献,然后递归右边即可
复杂度
然而这不是重点
如果我们移项,将左边的并入右边卷积式子,就有
如果我们将上面三部分分别看成三个多项式
容易看出
由于
都是无穷多项的多项式(形式幂级数),我们只需要
前面的项
用多项式求逆,可以得到
这样在
时间内就得到答案了
看出来了吗? 实际上都是指数型生成函数
从生成函数的角度入手
设
的EGF为
,
的EGF为
那么
无向图实际上就是由若干个连通块拼接而成,因此这实际上就是带标号对象重复拼接的过程(若干个F’卷在一起)。
因此
(因为连通块之间没有顺序之分,因此要除以i!)
也就是说
那么应用多项式求
就可以通过A’来求出F’了,进而求出f
复杂度同样是
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);
}