[LuoguP4841]城市规划(多项式ln+生成函数)
题面
求(n)个顶点的有标号连通简单无向图的个数(简单指的是无重边自环)。((n leq 10^5))
分析
(n)个点的简单无向图有(2^{inom{n}{2}})个,设G是所有无向图,那么G的EGF为
[G(x)=sum_{n=0}^{infin} 2^{inom{n}{2}} frac{x^n}{n!}
]
设连通图的生成函数为(C(x)),考虑枚举连通块个数(i),有
[G(x)=sum_{igeq 0}frac{1}{i!}C^i(x)=exp(C(x)
]
除(i!)是因为连通块内部的排列顺序无关。第二步用了(mathrm{e}^x)的泰勒展开式。多项式ln求出(C),复杂度(O(nlog n))
代码
#include<cstdio>
#include<cstring>
#define maxn 400000
#define mod 1004535809
using namespace std;
typedef long long ll;
inline ll fast_pow(ll x,ll k){
ll ans=1;
while(k){
if(k&1) ans=ans*x%mod;
x=x*x%mod;
k>>=1;
}
return ans;
}
inline ll inv(ll x){
return fast_pow(x,mod-2);
}
const ll G=3,invG=inv(3);
int rev[maxn*4+5];
void NTT(ll *x,int n,int type){
for(int i=0;i<n;i++) if(i<rev[i]) swap(x[i],x[rev[i]]);
for(int len=1;len<n;len*=2){
int sz=len*2;
ll gn1=fast_pow((type==1?G:invG),(mod-1)/sz);
for(int l=0;l<n;l+=sz){
int r=l+len-1;
ll gnk=1;
for(int i=l;i<=r;i++){
ll tmp=x[i+len];
x[i+len]=(x[i]-gnk*tmp%mod+mod)%mod;
x[i]=(x[i]+gnk*tmp%mod)%mod;
gnk=gnk*gn1%mod;
}
}
}
if(type==-1){
ll invn=inv(n);
for(int i=0;i<n;i++) x[i]=x[i]*invn%mod;
}
}
void poly_mul(ll *a,ll *b,ll *c,int n,int m){
static ll ta[maxn+5],tb[maxn+5];
int N=1,L=0;
while(N<n+m-1){
N*=2;
L++;
}
for(int i=0;i<n;i++) ta[i]=a[i];
for(int i=n;i<N;i++) ta[i]=0;
for(int i=0;i<m;i++) tb[i]=b[i];
for(int i=n;i<N;i++) tb[i]=0;
for(int i=0;i<N;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
NTT(ta,N,1);
NTT(tb,N,1);
for(int i=0;i<N;i++) c[i]=ta[i]*tb[i]%mod;
NTT(c,N,-1);
for(int i=n+m-1;i<N;i++) c[i]=0;
}
void poly_inv(ll *f,ll *g,int n){
static ll tmp[maxn+5];
if(n==1){
g[0]=inv(f[0]);
return;
}
poly_inv(f,g,(n+1)/2);
poly_mul(f,g,tmp,n,n);
poly_mul(tmp,g,tmp,n,n);
for(int i=0;i<n;i++) g[i]=(2*g[i]-tmp[i]+mod)%mod;
}
void poly_deriv(ll *f,ll *g,int n){
for(int i=1;i<n;i++) g[i-1]=f[i]*i%mod;
g[n-1]=0;
}
void poly_inter(ll *f,ll *g,int n){
for(int i=n-1;i>=1;i--) g[i]=f[i-1]*inv(i)%mod;
g[0]=0;
}
void poly_ln(ll *f,ll *g,int n){
static ll invf[maxn+5];
poly_inv(f,invf,n);
poly_deriv(f,g,n);
poly_mul(g,invf,g,n,n);
poly_inter(g,g,n*2);
for(int i=n;i<n*2;i++) g[i]=0;
}
void poly_exp(ll *f,ll *g,int n){
static ll lng[maxn+5];
if(n==1){
g[0]=1;
return;
}
poly_exp(f,g,(n+1)/2);
poly_ln(g,lng,n);
for(int i=0;i<n;i++) lng[i]=(f[i]-lng[i]+mod)%mod;
lng[0]++;
poly_mul(g,lng,g,n,n);
for(int i=n;i<n*2;i++) g[i]=0;
}
int n;
ll fact[maxn+5];
ll c[maxn+5],f[maxn+5];
int main(){
scanf("%d",&n);
fact[0]=1;
for(int i=1;i<=n;i++) fact[i]=fact[i-1]*i%mod;
for(int i=0;i<=n;i++) c[i]=fast_pow(2,1ll*i*(i-1)/2)*inv(fact[i])%mod;
poly_ln(c,f,n+1);
printf("%lld
",f[n]*fact[n]%mod);
}