有标号DAG计数(生成函数)
题解时间
首先考虑暴力,很容易得出 $ f[ i ] = sumlimits_{ j = 1 }^{ i } ( -1 )^{ j - 1 } inom{ i }{ j } 2^{ j( i - j ) } f[ i-j ] $ 。
相当于枚举度数为0的节点的个数,向不在这个集合里的点任意连边,之后需要容斥。
考虑如何优化。
$ j(i-j) = frac{ i^{ 2 } }{ 2 } - frac{ j^{ 2 } }{ 2 } - frac{ ( i - j )^{ 2 } }{ 2 } $ 。
$ f[ i ] = sumlimits_{ j = 1 }^{ i } ( -1 )^{ j - 1 } frac{ i! }{ j!(i-j)! } frac{ ( sqrt{ 2 } )^{ i^{ 2 } } }{ ( sqrt{ 2 } )^{ j^{ 2 } } ( sqrt{ 2 } )^{ (i-j)^{ 2 } } } f[ i-j ] $ 。
构造 $ F(x) = sumlimits_{ i = 0 }^{n} frac{ f_{ i } }{ i! sqrt{ 2 }^{ i^{ 2 } } } x^{ i } , G(x) = sumlimits_{ i = 0 }^{n} frac{ (-1)^{ i - 1 } }{ i! sqrt{ 2 }^{ i^{ 2 } } } x^{ i } $ ,
有 $ F( x ) = F( x )G( x ) + 1 $ ,直接求逆。
如果要求图必须是弱连通呢?
直接对EGF取ln就好。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
namespace RKK
{
const int N=300011,maxn=262144;
const int mo=998244353,G=3,sqrt2=116195171;
lint add(lint a,lint b){return (a+=b)>=mo?a-mo:a;}
lint doadd(lint &a,lint b){if((a+=b)>=mo) a-=mo;}
lint fpow(lint a,lint p){lint ret=1;while(p){if(p&1ll) ret=ret*a%mo;a=a*a%mo;p>>=1;}return ret;}
lint wg[N],iwg[N];int rev[N],lastlen;
inline void init(){for(int i=1;i<maxn;i<<=1) wg[i]=fpow(G,(mo-1)/(i<<1)),iwg[i]=fpow(wg[i],mo-2);}
inline void getrev(int len){for(int i=1;i<len;i++)rev[i]=(rev[i>>1]>>1)|((i&1)*(len>>1));}
inline void ntt(lint *a,int len,int tp)
{
if(lastlen!=len) getrev(len),lastlen=len;
for(int i=0;i<len;i++)if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=1;i<len;i<<=1)
{
lint w0=(~tp)?wg[i]:iwg[i];
for(int j=0;j<len;j+=(i<<1))
{
lint w=1;
for(int k=0;k<i;k++,(w*=w0)%=mo)
{
lint w1=a[j+k],w2=w*a[j+k+i]%mo;
a[j+k]=add(w1,w2),a[j+k+i]=add(w1,mo-w2);
}
}
}
lint ilen=fpow(len,mo-2);
if(tp==-1)for(int i=0;i<len;i++) (a[i]*=ilen)%=mo;
}
namespace poly
{
namespace inv
{
lint a[N],b[N];
void work(lint *f,lint *g,int n)
{
g[0]=fpow(f[0],mo-2);
for(int len=1;len<n<<1;len<<=1)
{
memcpy(a,f,len*8),memcpy(b,g,len*8);
ntt(a,len<<1,1),ntt(b,len<<1,1);
for(int i=0;i<len<<1;i++) g[i]=b[i]*(2ll-a[i]*b[i]%mo+mo)%mo;
ntt(g,len<<1,-1),memset(g+len,0,len*8);
}
memset(a,0,n*16),memset(b,0,n*16);
}
}
namespace ln
{
lint a[N],b[N];
void de(lint *f,int n){for(int i=0;i<n;i++) f[i]=f[i+1]*(i+1)%mo;f[n-1]=0;}
void ide(lint *f,int n){for(int i=n-2;i>=0;i--) f[i+1]=f[i]*fpow(i+1,mo-2)%mo;f[0]=0;}
void work(lint *f,lint *g,int n)
{
int len=n;
memcpy(a,f,len*8),de(a,len);
inv::work(f,b,len);
ntt(a,len<<1,1),ntt(b,len<<1,1);
for(int i=0;i<len<<1;i++) g[i]=a[i]*b[i]%mo;
ntt(g,len<<1,-1),ide(g,len);
memset(a,0,len*16),memset(b,0,len*16);
}
}
}
int n;
lint inv[N],fac[N],ifac[N];
void sieve(int n)
{
inv[0]=inv[1]=1;for(int i=2;i<=n;i++) inv[i]=inv[mo%i]*(mo-mo/i)%mo;
fac[0]=1;for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mo;
ifac[0]=1;for(int i=1;i<=n;i++) ifac[i]=ifac[i-1]*inv[i]%mo;
}
lint f[N],g[N],h[N];
int main()
{
scanf("%d",&n);init();
int len=1;while(len<=n) len<<=1;
sieve(len);
g[0]=1;for(int i=1;i<len;i++)
{
g[i]=ifac[i]*fpow(fpow(sqrt2,1ll*i*i%(mo-1)),mo-2)%mo;
if(i&1) g[i]=mo-g[i];
}
poly::inv::work(g,f,len);//求出不保证联通的
for(int i=0;i<len;i++) f[i]=f[i]*fpow(sqrt2,1ll*i*i%(mo-1))%mo;//不乘fac因为还要用egf直接去ln
poly::ln::work(f,h,len);
for(int i=1;i<=n;i++) printf("%lld
",h[i]*fac[i]%mo);
return 0;
}
}
int main(){return RKK::main();}