【省选模拟】基因解码 (斯特林数)

C F 715 E CF715E CF715E

  • 对于全部确定的情况,算出 n n n - 环数 即可
    对于全部不确定的情况, a n s i = s n , i n ! ans_i=s_{n,i}n! ansi=sn,in!
    否则考虑 p i p_i pi q i q_i qi 连边,原图相当于若干条链(以及环,已经成环可以不管了),形如 ( 0 , 0 ) , ( a , 0 ) , ( 0 , a ) , ( a , b ) (0,0),(a,0),(0,a),(a,b) (0,0),(a,0),(0,a),(a,b),对于第 4 类对方案数没有贡献
    分类记为 a , b , c a,b,c a,b,c b b b 类可以自己和自己拼接,枚举其个数以及环数,剩余的考虑与 ( 0 , 0 ) (0,0) (0,0) 拼接,注意到 ( a , 0 ) (a,0) (a,0) 可以与 ( 0 , 0 ) (0,0) (0,0) 拼接成 ( 0 , 0 ) (0,0) (0,0),写成对其环个数的 O G F OGF OGF
    拼接时 b b b 带标号,先让 a a a 无标号,最后考虑其标号,方案数是个插板
    F ( x ) = ∑ i = 0 b x i ∑ j = i b ( b j ) s j , i ( a − 1 + j − i j − i ) ( j − i ) ! F(x)=\sum_{i=0}^bx^i\sum_{j=i}^b\binom{b}{j}s_{j,i}\binom{a-1+j-i}{j-i}(j-i)! F(x)=i=0bxij=ib(jb)sj,i(jia1+ji)(ji)!
    c c c 类同理, a a a 类的 O G F OGF OGF 也可以简单写出(考虑标号)
    G ( x ) = ∑ i = 0 a a ! s a , i x i G(x)=\sum_{i=0}^a a!s_{a,i}x^i G(x)=i=0aa!sa,ixi
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Mul(int &a, int b){ a = mul(a,b); }
void Add(int &a, int b){ a = add(a,b); }
void Dec(int &a, int b){ a = dec(a,b); }
int ksm(int a, int b){ int as=1; for(;b;b>>=1,Mul(a,a))if(b&1)Mul(as,a); return as; }
cs int N = 5e3 + 50;
int n, a[N], b[N], in[N], out[N];
int A, B, C, D, to[N];
int S[N][N], f[N], g[N], h[N];
int fc[N], ifc[N];
int Binom(int n, int m){ if(n<0||m<0||n<m) return 0; return mul(fc[n],mul(ifc[n-m],ifc[m])); }
int anc[N], sz[N], ec[N];
int fnd(int x){ return x == anc[x] ? x : anc[x] = fnd(anc[x]); }
void mrg(int x, int y){
	int fx = fnd(x), fy = fnd(y); ++ec[fx];
	if(fx==fy) return; 
	ec[fx]+=ec[fy]; sz[fx]+=sz[fy];
	anc[fy] = fx;
}
int main(){
	#ifdef FSYolanda
	freopen("1.in","r",stdin);
	#endif
	scanf("%d",&n);
	for(int i=1; i<=n; i++)
	scanf("%d",&a[i]);
	for(int i=1; i<=n; i++)
	scanf("%d",&b[i]);
	for(int i=1; i<=n; i++) anc[i]=i,sz[i]=1;
	for(int i=1; i<=n; i++) if(a[i] && b[i]) mrg(a[i],b[i]);
	
	static bool ea[N], eb[N], da[N], db[N];
	for(int i=1,u,v; i<=n; i++){
		u=fnd(a[i]),v=fnd(b[i]);
		if(a[i] && b[i] && u==v){
			if(u==a[i] && ec[u]==sz[u]) ++D;
		} a[i]=u; b[i]=v;
		if(a[i] && b[i]!=a[i]) 
		ea[a[i]] = true, da[a[i]] = !!b[i];
		if(b[i] && b[i]!=a[i])
		eb[b[i]] = true, db[b[i]] = !!a[i];
	} 
	for(int i=1; i<=n; i++)  
	if(!a[i] && !b[i]) ++A;
	for(int i=1; i<=n; i++){
		if(ea[i] && eb[i] && !da[i] && !db[i]) ++A;
		else{
			if(ea[i] && !da[i]) ++B;
			if(eb[i] && !db[i]) ++C;
		}
	}
	S[0][0] = 1;
	for(int i=1; i<=n; i++)
	for(int j=1; j<=i; j++)
	S[i][j]=add(S[i-1][j-1],mul(S[i-1][j],i-1));
	fc[0]=ifc[0]=fc[1]=ifc[1]=1;
	for(int i=2; i<=n; i++) fc[i]=mul(fc[i-1],i);
	ifc[n]=ksm(fc[n],Mod-2);
	for(int i=n-1; i>=2; i--) ifc[i]=mul(ifc[i+1],i+1);
	
	for(int i=0; i<=B; i++){
		if(!A) f[i]=S[B][i];
		else for(int j=i; j<=B; j++)
		Add(f[i],mul(mul(Binom(B,j),S[j][i]),mul(Binom(A-1+B-j,A-1),fc[B-j])));
	}
	for(int i=0; i<=C; i++){
		if(!A) g[i]=S[C][i];
		else for(int j=i; j<=C; j++)
		Add(g[i],mul(mul(Binom(C,j),S[j][i]),mul(Binom(A-1+C-j,A-1),fc[C-j])));
	}
	static int ans[N], tmp[N]; 
	for(int i=0; i<=B; i++)
	for(int j=0; j<=C; j++)
	Add(tmp[i+j],mul(f[i],g[j]));
	for(int i=0; i<=A; i++)
	h[i] = mul(fc[A],S[A][i]);
	for(int i=0; i<=B+C; i++)
	for(int j=0; j<=A; j++)
	Add(ans[i+j],mul(h[j],tmp[i]));
	for(int i=n; i>=D; i--) ans[i] = ans[i-D];
	for(int i=D-1; i>=0; i--) ans[i] = 0;
	for(int i=0; i<n; i++)
	cout<<ans[n-i]<<" ";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值