P6295 有标号 DAG 计数

P6295 有标号 DAG 计数

题意

n 个点有标号弱联通 DAG 数量。

推导

fi 表示 i 个点有标号 DAG 数量(不保证弱联通),有:

f(i)=j=1i(ij)(1)j1f(ij)2j(ij)

意义为选至少 j 个度数为零的点,向剩下的 ij 个点随便连有向边,容斥一下就得到了上式。

下面进行推导。根据一个 trick:

j(ij)=(i2)(j2)(ij2)

所以有:

f(i)=j=1ii!j!(ij)!(1)j1f(ij)2(i2)2(j2)2(ij2)f(i)i!2(i2)=j=1i(1)j1j!2(j2)f(ij)(ij)!2(ij2)

F(x)=i=0f(i)i!2(i2)G(x)=i=1(1)i1i!2(i2)

则有

F(x)=F(x)G(x)+1

加 1 是因为常数项为 1.

解得

F(x)=11G(x)

根据 F 的定义,我们解出 F 后乘上 2(i2) 即为 f 的 EGF。根据多项式 ln 和 exp 的组合意义,我们将得到的 EGF ln 一下即可得到题目要求的弱联通的 DAG 数量的 EGF。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
using namespace std;
inline int read(){
	int w=0,x=0;char c=getchar();
	while(!isdigit(c))w|=c=='-',c=getchar();
	while(isdigit(c))x=x*10+(c^48),c=getchar();
	return w?-x:x;
}
namespace star
{
	const int maxn=4e5+10,mod=998244353,g=3,gi=998244354/3;
	inline int fpow(int a,long long b){int ans=1;for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod; return ans;}
	struct NTT{
		int r[maxn],lim;
		inline void getr(int li){lim=li;for(int i=0;i<=lim;i++) r[i]=(r[i>>1]>>1)|((i&1)*(lim>>1));}
		void operator () (int *a,int type) const {
			for(int i=0;i<lim;i++) if(i<r[i]) swap(a[i],a[r[i]]);
			for(int mid=1;mid<lim;mid<<=1){
				int rt=fpow(type==1?g:gi,(mod-1)/(mid<<1));
				for(int r=mid<<1,j=0;j<lim;j+=r){
					int p=1;
					for(int k=0;k<mid;k++,p=1ll*p*rt%mod){
						int x=a[j+k],y=1ll*a[j+k+mid]*p%mod;
						a[j+k]=(x+y)%mod,a[j+k+mid]=(x-y+mod)%mod;
					}
				}
			}
			if(type==-1) for(int p=fpow(lim,mod-2),i=0;i<lim;i++) a[i]=1ll*a[i]*p%mod;
		}
	}ntt;
	void Inv(const int *a,int *ans,int n){
		if(n==1) return ans[0]=fpow(a[0],mod-2),ans[1]=0,void();
		static int res[maxn];
		Inv(a,ans,n>>1);
		int lim=n<<1;
		ntt.getr(lim);
		for(int i=0;i<n;i++) res[i]=a[i];
		for(int i=n;i<lim;i++) ans[i]=res[i]=0;
		ntt(ans,1),ntt(res,1);
		for(int i=0;i<lim;i++) ans[i]=ans[i]*(2-1ll*ans[i]*res[i]%mod+mod)%mod;
		ntt(ans,-1);
		for(int i=n;i<lim;i++) ans[i]=0;
	}
	inline void deri(const int *a,int *ans,int n){for(int i=1;i<n;i++) ans[i-1]=1ll*a[i]*i%mod;ans[n-1]=0;}
	inline void inte(const int *a,int *ans,int n){for(int i=n-1;i;i--) ans[i]=1ll*a[i-1]*fpow(i,mod-2)%mod;ans[0]=0;}
	inline void ln(const int *a,int *ans,int n){
		static int res[maxn];
		Inv(a,ans,n);
		deri(a,res,n);
		int lim=n<<1;
		ntt.getr(lim);
		ntt(ans,1),ntt(res,1);
		for(int i=0;i<lim;i++) res[i]=1ll*ans[i]*res[i]%mod,ans[i]=0;
		ntt(res,-1);
		for(int i=n;i<lim;i++) res[i]=0;
		inte(res,ans,n);
	}
	int a[maxn],b[maxn],n,inv[maxn],mul[maxn];
	inline void work(){
		n=read();
		inv[0]=mul[0]=1;for(int i=1;i<=n;i++) mul[i]=1ll*mul[i-1]*i%mod;
		inv[n]=fpow(mul[n],mod-2);for(int i=n-1;i;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
		for(int i=1;i<=n;i++) a[i]=(i&1?mod-1ll:1ll)*inv[i]%mod*fpow(fpow(2,1ll*i*(i-1)/2),mod-2)%mod;
		a[0]=1;
		int lim=1;for(;lim<=n;lim<<=1);
		Inv(a,b,lim);
		for(int i=0;i<=n;i++) b[i]=1ll*b[i]*fpow(2,1ll*i*(i-1)/2)%mod,a[i]=0;
		for(int i=n+1;i<lim;i++) a[i]=b[i]=0;
		ln(b,a,lim);
		for(int i=1;i<=n;i++) printf("%lld\n",1ll*a[i]*mul[i]%mod);
	}
}
signed main(){
	star::work();
	return 0;
}
posted @   Star_Cried  阅读(85)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示