有标号DAG计数(生成函数)

有标号DAG计数(生成函数)

luogu

题解时间

首先考虑暴力,很容易得出 $ f[ i ] = \sum\limits_{ j = 1 }^{ i } ( -1 )^{ j - 1 } \binom{ 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 ] = \sum\limits_{ 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) = \sum\limits_{ i = 0 }^{n} \frac{ f_{ i } }{ i! \sqrt{ 2 }^{ i^{ 2 } } } x^{ i } , G(x) = \sum\limits_{ 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\n",h[i]*fac[i]%mo);
	return 0;
}
}
int main(){return RKK::main();}
posted @ 2020-07-25 20:36  RikukiIX  阅读(233)  评论(0编辑  收藏  举报