[LuoguP4841]城市规划(多项式ln+生成函数)

[LuoguP4841]城市规划(多项式ln+生成函数)

题面

\(n\)个顶点的有标号连通简单无向图的个数(简单指的是无重边自环)。(\(n \leq 10^5\))

分析

\(n\)个点的简单无向图有\(2^{\binom{n}{2}}\)个,设G是所有无向图,那么G的EGF为

\[G(x)=\sum_{n=0}^{\infin} 2^{\binom{n}{2}} \frac{x^n}{n!} \]

设连通图的生成函数为\(C(x)\),考虑枚举连通块个数\(i\),有

\[G(x)=\sum_{i\geq 0}\frac{1}{i!}C^i(x)=\exp(C(x) \]

\(i!\)是因为连通块内部的排列顺序无关。第二步用了\(\mathrm{e}^x\)的泰勒展开式。多项式ln求出\(C\),复杂度\(O(n\log 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\n",f[n]*fact[n]%mod);
}
posted @ 2020-08-03 16:12  birchtree  阅读(184)  评论(0编辑  收藏  举报