Loading

指数生成函数(EGF)

熟知指数生成函数(以下简称为EGF)定义为:

\[F(x)=\sum\limits_{n=0}^\infty\dfrac{a_n}{n!}x^n \]

并且两个EGF相乘为 \(F(x)G(x)\) 是数列 \(\left\{\sum\limits^n_{k=0}\dbinom{n}{k}a^kb^{n-k}\right\}_{n=0}^\infty\) 的EGF

因为我们将 \(\text{e}^x\) 再原点处进行泰勒展开得到了数列 \(\{1,1,\cdots\}_{n=0}^\infty\) 的无穷级数形式

所以如果一个数列为 \(\{1,1,\cdots\}_{n=0}^\infty\) 那么它EGF的封闭形式为 \(\text{e}^x\)


一个EGF \(F(x)=\sum\limits_{n=0}^\infty\dfrac{a_n}{n!}x^n\) 同时也是数列 \(\left\{\dfrac{a_n}{n!}\right\}_{n=0}^\infty\) 的OGF

我们考虑一个长度为 \(n\) 数列的排列数(数列中的数互不相同)

显而易见,排列数是 \(n!\)

写成EGF的形式也就是

\[F(x)=\sum\limits_{n=0}^\infty\dfrac{n!}{n!}x_n=\dfrac{1}{1-x} \]

那么我们再考虑一个长度为 \(n\) 的数列的圆排列数

显然对于每个排列,我们将其旋转 \(n\) 次的到的圆排列相同

显然,圆排列数是 \((n-1)!\)

写成EGF的形式:

\[\begin{align*} G(x)&=\sum\limits_{n=1}^\infty\dfrac{(n-1)!}{n!}x_n \\ &=\sum\limits_{n=1}^\infty\dfrac{x_n}{n} \\ &=\ln\left(\dfrac{1}{1-x}\right) \end{align*} \]

是不是发现了显然可以 \(\exp\)

\(\exp G(x)=F(x)\)

在学习群论的时候,我们知道每个置换 \(a\in S_n\) 或是一个循环置换,或是若干个不相交的循环置换的乘积

此时我们把每个排列看成一个置换

长度为 \(n\) 的排列数方案数即是把其分成 \(k\) 个集合,然后每个集合的圆排列的方案数的积


P4841 [集训队作业2013]城市规划

熟知 \(n\) 个点的有标号无向图的个数是 \(2^\binom{n}{2}\)

每个无向图可以看作若干个集合构成,每个集合里的元素同属一个连通图

\(n\) 个点的有标号无向图的EGF是 \(F(x)\)\(n\) 个点的有标号无向连通图的EGF是 \(G(x)\)

此时我们很容易仿照上文得到 \(\exp G(x)=F(x)\),即 \(G(x)=\ln F(x)\)

直接多项式 \(\ln\) 即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 1000001
#define INF 1100000000
#define Kafuu return
#define Chino 0
#define fx(l,n) inline l n
#define set(l,n,ty,len) memset(l,n,sizeof(ty)*len)
#define cpy(f,t,ty,len) memcpy(t,f,sizeof(ty)*len)
#define R register int
#define C const
#define int long long
using namespace std;
C int mod=1004535809,pr=3;
int n,br[N],ppr[N],x=1,invp,invx,A[N],B[N],fc[N];
fx(int,gi)(){
	char c=getchar();int s=0,f=1;
	while(c<'0'||c>'9'){
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') s=(s<<3)+(s<<1)+(c-'0'),c=getchar();
	return s*f;
}
fx(int,pow)(int a,int b=mod-2){
	int ans=1;
	while(b){
		if(b&1) (ans*=a)%=mod;
		(a*=a)%=mod;
		b>>=1;
	}
	return ans;
}
fx(void,NTT)(int *f,C short r,C int x){
	R len,hl,exp,uni,s,i;
	for(i=0;i<x;i++) br[i]=(br[i>>1]>>1)|((i&1)?x>>1:0);
	for(i=0;i<x;i++) if(i<br[i]) swap(f[i],f[br[i]]);
	for(len=2,hl=1;len<=x;hl=len,len<<=1){
		exp=pow(r==1?pr:invp,(mod-1)/len);
		for(i=1;i<hl;i++) ppr[i]=ppr[i-1]*exp%mod;
		for(s=0;s<x;s+=len){
			for(i=0;i<hl;i++){
				uni=ppr[i]*f[i|s|hl]%mod;
				f[i|s|hl]=(f[i|s]-uni+mod)%mod;
				f[i|s]=(f[i|s]+uni)%mod;
			}
		}
	}
	if(r==-1){
		invx=pow(x);
		for(i=0;i<x;i++) (f[i]*=invx)%=mod;
	}
}
fx(void,INV)(int *f,const int x){
	static int le[N],ri[N],inv[N];
	inv[0]=pow(f[0]);
	for(R len=2,hl=1,o;len<=x;hl=len,len<<=1){
		for(o=0;o<hl;o++) le[o]=(inv[o]<<1)%mod;
		cpy(f,ri,int,len);
		NTT(inv,1,len<<1);NTT(ri,1,len<<1);
		for(o=0;o<(len<<1);o++) (((inv[o]*=inv[o])%=mod)*=ri[o])%=mod;
		NTT(inv,-1,len<<1);
		for(o=0;o<len;o++) inv[o]=(le[o]-inv[o]+mod)%mod;
		set(inv+len,0,int,len);
	}
	cpy(inv,f,int,n);
}
fx(void,DER)(int *f,C int len){
	for(int i=1;i<len;i++) f[i-1]=f[i]*i%mod;
	f[len-1]=0;
}
fx(void,INT)(int *f,C int len){
	for(R i=len-1;i>=1;i--) f[i]=f[i-1]*pow(i)%mod;
	f[0]=0;
}
signed main(){
	ios::sync_with_stdio(0);
	n=gi();
	n+=1;fc[0]=1;
	for(R i=1;i<n;i++) fc[i]=fc[i-1]*i%mod;
	for(R i=0;i<n;i++) A[i]=pow(2,i*(i-1)/2)*pow(fc[i])%mod;
	cpy(A,B,int,n);
	while(x<n) x<<=1;
	invp=pow(pr);ppr[0]=1;
	INV(A,x);DER(B,n);
	while(x<n+n) x<<=1;
	NTT(A,1,x);NTT(B,1,x);
	for(R i=0;i<x;i++) (A[i]*=B[i])%=mod;
	NTT(A,-1,x);INT(A,n);
	printf("%lld",A[n-1]*fc[n-1]%mod);
}
posted @ 2021-02-26 18:14  zythonc  阅读(449)  评论(0编辑  收藏  举报