洛谷 P6295 有标号 DAG 计数 题解

题目传送门

题目大意: n n n 个点的弱连通带标号 DAG \text{DAG} DAG 数量。

题解

容易发现,对答案的 EGF \text{EGF} EGF exp ⁡ \exp exp 可以得到任意带标号 DAG \text{DAG} DAG 数量,也就是说,求出任意带标号 DAG \text{DAG} DAG 数量再求 ln ⁡ \ln ln 就可以得到答案。

f n f_n fn 表示 n n n 个点的任意带标号 DAG \text{DAG} DAG 数量,考虑枚举 i i i 表示至少有 i i i 个点入度为 0 0 0,容斥一下可以得到 f f f 的转移式:
f n = ∑ i = 1 n ( − 1 ) i + 1 C n i 2 i ( n − i ) f n − i f_n=\sum_{i=1}^n (-1)^{i+1} C_n^i 2^{i(n-i)}f_{n-i} fn=i=1n(1)i+1Cni2i(ni)fni

C n i C_n^i Cni 是选出入度为 0 0 0 的点, 2 i ( n − i ) 2^{i(n-i)} 2i(ni) 是这些点与剩下点之间的边可连可不连,以及剩下的点也构成了 DAG \text{DAG} DAG,方案为 f n − i f_{n-i} fni

发现除了 2 i ( n − i ) 2^{i(n-i)} 2i(ni) 这一项,其他都可以拆成卷积的形式,这里用到一个小技巧,考虑 i ( n − i ) i(n-i) i(ni) 的组合意义,可以拆成 C n 2 − C i 2 − C n − i 2 C_n^2-C_i^2-C_{n-i}^2 Cn2Ci2Cni2,带进去得到:
f n = ∑ i = 1 n ( − 1 ) i + 1 n ! i ! ( n − i ) ! 2 C n 2 − C i 2 − C n − i 2 f n − i f n n ! 2 C n 2 = ∑ i = 1 ( − 1 ) i + 1 i ! 2 C i 2 × f n − i ( n − i ) ! 2 C n − i 2 \begin{aligned} f_n&=\sum_{i=1}^n (-1)^{i+1} \frac {n!} {i!(n-i)!}2^{C_n^2-C_i^2-C_{n-i}^2}f_{n-i}\\ \frac {f_n} {n!2^{C_n^2}}&=\sum_{i=1}\frac {(-1)^{i+1}} {i!2^{C_i^2}}\times \frac {f_{n-i}} {(n-i)!2^{C_{n-i}^2}} \end{aligned} fnn!2Cn2fn=i=1n(1)i+1i!(ni)!n!2Cn2Ci2Cni2fni=i=1i!2Ci2(1)i+1×(ni)!2Cni2fni

F ( x ) = ∑ i = 0 f i i ! 2 C i 2 , G ( x ) = ∑ i = 1 ( − 1 ) i + 1 i ! 2 C i 2 F(x)=\sum_{i=0} \dfrac {f_i} {i!2^{C_i^2}},G(x)=\sum_{i=1} \dfrac {(-1)^{i+1}} {i!2^{C_i^2}} F(x)=i=0i!2Ci2fi,G(x)=i=1i!2Ci2(1)i+1,将上式写成封闭形式:
F = F G + 1 F = 1 1 − G \begin{aligned} F&=FG+1\\ F&=\frac 1 {1-G} \end{aligned} FF=FG+1=1G1

求出 G G G 然后求个逆就得到 F F F 了,再求个 ln ⁡ \ln ln 就得到答案。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 300010
#define mod 998244353
#define bin(x) (1<<(x))
#define MS(f,x) memset(f,0,4<<(x))
#define CP(f,g,x) memcpy(f,g,(x)<<2)

int n,F[maxn],G[maxn];
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
int inv[maxn],w[maxn];void prep(int lg){int N=bin(lg);
	inv[1]=1;for(int i=2;i<=N;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1,wn;i<N;i<<=1){
		w[i]=1;wn=ksm(3,(mod-1)/(i<<1));
		for(int j=1;j<i;j++)w[i+j]=1ll*w[i+j-1]*wn%mod;
	}
}
int limit,r[maxn];
void InitR(int lg){for(int i=1;i<bin(lg);i++)r[i]=(r[i>>1]>>1)|((i&1)<<(lg-1));}
int add(int x){return x>=mod?x-mod:x;}
int dec(int x){return x<0?x+mod:x;}
void ntt(int *f,int lg,int type=0){
	limit=bin(lg);if(type)reverse(f+1,f+limit);
	for(int i=1;i<limit;i++)if(i<r[i])swap(f[i],f[r[i]]);
	for(int mid=1,t;mid<limit;mid<<=1)for(int j=0;j<limit;j+=(mid<<1))for(int i=0;i<mid;i++)
	{t=1ll*f[j+i+mid]*w[mid+i]%mod;f[j+i+mid]=dec(f[j+i]-t);f[j+i]=add(f[j+i]+t);}
	if(type)for(int i=0;i<limit;i++)f[i]=1ll*f[i]*inv[limit]%mod;
}
void NTT(int *f,int *g,int ln,int Ty=0){
	int lg=ceil(log2(Ty?ln:ln*2-1));InitR(lg);ntt(f,lg);ntt(g,lg);
	for(int i=0;i<bin(lg);i++)f[i]=1ll*f[i]*g[i]%mod;ntt(f,lg,1);
}
int A[maxn],B[maxn],C[maxn],D[maxn];
void getinv(int *f,int *g,int ln){
	if(ln==1){g[0]=ksm(f[0],mod-2);return;}getinv(f,g,(ln+1)>>1);
	int lg=ceil(log2(ln*2-1));MS(A,lg);MS(B,lg);CP(A,f,ln);CP(B,g,ln);
	InitR(lg);ntt(A,lg);ntt(B,lg);for(int i=0;i<bin(lg);i++)B[i]=1ll*B[i]*(2-1ll*A[i]*B[i]%mod+mod)%mod;
	ntt(B,lg,1);CP(g,B,ln);
}
void Dao(int *f,int *g,int ln){for(int i=0;i<ln-1;i++)g[i]=1ll*f[i+1]*(i+1)%mod;g[ln-1]=0;}
void Jifen(int *f,int *g,int ln){for(int i=ln-1;i>0;i--)g[i]=1ll*f[i-1]*inv[i]%mod;g[0]=0;}
void getln(int *f,int *g,int ln){
	int lg=ceil(log2(ln*2-1));MS(C,lg);MS(D,lg);
	Dao(f,C,ln);getinv(f,D,ln);NTT(C,D,ln);Jifen(C,g,ln);
}
int fac[maxn],inv_fac[maxn];
void FacInit(){
	fac[0]=inv_fac[0]=1;
	for(int i=1;i<=n;i++){
		fac[i]=1ll*fac[i-1]*i%mod;
		inv_fac[i]=1ll*inv_fac[i-1]*inv[i]%mod;
	}
}
int C2(int x){return ksm(2,1ll*x*(x-1)/2%(mod-1));}

int main()
{
	scanf("%d",&n);n++;
	prep(ceil(log2(n<<1)));
	FacInit();
	G[0]=1;for(int i=1;i<n;i++){
		G[i]=1ll*inv_fac[i]*ksm(C2(i),mod-2)%mod;
		if(i&1)G[i]=mod-G[i];
	}
	getinv(G,F,n);
	for(int i=0;i<n;i++)F[i]=1ll*F[i]*C2(i)%mod;
	getln(F,G,n);
	for(int i=1;i<n;i++)printf("%lld\n",1ll*G[i]*fac[i]%mod);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值