指数生成函数(EGF)
熟知指数生成函数(以下简称为EGF)定义为:
并且两个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的形式也就是
那么我们再考虑一个长度为 \(n\) 的数列的圆排列数
显然对于每个排列,我们将其旋转 \(n\) 次的到的圆排列相同
显然,圆排列数是 \((n-1)!\)
写成EGF的形式:
是不是发现了显然可以 \(\exp\) ?
\(\exp G(x)=F(x)\)
在学习群论的时候,我们知道每个置换 \(a\in S_n\) 或是一个循环置换,或是若干个不相交的循环置换的乘积
此时我们把每个排列看成一个置换
长度为 \(n\) 的排列数方案数即是把其分成 \(k\) 个集合,然后每个集合的圆排列的方案数的积
熟知 \(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);
}