CF715E Complete the Permutations(计数,斯特林数)

先考虑 \(q_i=i\)\(i\)\(p_i\) 连边,操作次数显然是 \(n\) 减去环数。

再考虑 \(q_i\) 给定。设 \(q\) 的逆排列 \(q'\)\(i\)\(q'_{p_i}\) 连边。

可以看成是第一个排列的 \(i\) 向第二个排列的 \(p_i\) 连边,第二个排列的 \(i\) 向第一个排列的 \(q'_i\) 连边。

考虑原问题。此时已经有了一些给定的链(可能退化成点),有 \(c_{0,0}\) 个第一个排列到第一个排列的,\(c_{0,1}\) 个第一个排列到第二个排列的,\(c_{1,0}\)\(c_{1,1}\) 同理,要把它们连成一堆环,要求相邻两个相接的地方来自不同的排列(比如那 \(c_{0,0}\) 个后面只能接那 \(c_{1,0}+c_{1,1}\) 个,同样不能单独成环,但是那 \(c_{0,1}\) 个是可以单独成环的)。

注意到一定有 \(c_{0,0}=c_{1,1}\)。考虑一个 \(c_{0,0}\),由于不能单独成环,后面肯定接一个 \(c_{1,0}\) 或者 \(c_{1,1}\)。若接了 \(c_{1,0}\),那么这个环也还不能结束,需要接着接直到出现 \(c_{1,1}\) 为止,所以放一个 \(c_{0,0}\) 肯定消耗恰好一个 \(c_{1,1}\)。又因为一定有合法方案,得证。

上面的过程也说明,我们可以先只考虑 \(c_{0,0}\)\(c_{1,1}\),配对起来成环,然后把 \(c_{0,1},c_{1,0}\) 插空塞进去或者新开一些环。

\(f_i\) 表示用 \(c_{0,0},c_{1,1}\) 组成 \(i\) 个环的方案数。这个就是配对后乘上个第 \(c_{0,0}\) 行第一类斯特林数。

显然 \(c_{0,1}\)\(c_{1,0}\) 不相邻(所以一个新开的环中也只可能有它们的一种),是独立的。下文以 \(c_{0,1}\) 为例。

若我们选了 \(i\)\(c_{0,1}\) 来新开环,那么新开环的方案数是个第 \(i\) 行第一类斯特林数。剩下的 \(c_{0,1}-i\) 会分配到 \(c_{0,0}\) 个空位里,方案数是

\[(c_{0,1}-i)![x^{c_{0,1}-i}](\sum_{k\ge 0}\frac{x^k}{k!}\times k!)^{c_{0,0}}=(c_{0,1}-i)!\binom{c_{0,0}+c_{0,1}-i-1}{c_{0,1}-i} \]

上面这个式子也可以考虑组合意义:后面显然是插板法,众所周知插板法不能改变顺序,就乘个阶乘。

然后把三个东西卷在一起,暴力都可以。

总时间复杂度 \(O(n^3)\) 或者更优。感觉这个做法的优化空间挺大的?

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=255,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,p[maxn],q[maxn],tmp[maxn],to[maxn*2],deg[maxn*2],cnt[2][2],tot,f[maxn][maxn],fac[maxn*2],inv[maxn*2],invfac[maxn*2],S[maxn][maxn],a[maxn],b[maxn],c[maxn],ans[maxn];
bool vis[maxn*2];
inline int C(int n,int m){
	if(n<m || n<0 || m<0) return 0;
	return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
int main(){
	n=read();
	FOR(i,1,n) p[i]=read();
	FOR(i,1,n) if(q[i]=read()) tmp[q[i]]=i;
	FOR(i,1,n) q[i]=tmp[i];
	FOR(i,1,n) if(p[i]) to[i]=p[i]+n,deg[to[i]]++;
	FOR(i,1,n) if(q[i]) to[i+n]=q[i],deg[to[i+n]]++;
	FOR(i,1,2*n) if(!deg[i]){
		int u=i;
		while(to[u]) vis[u]=true,u=to[u];
		vis[u]=true;
		cnt[i>n][u>n]++;
	}
	FOR(i,1,2*n) if(!vis[i]){
		tot++;
		int u=i;
		while(!vis[u]) vis[u]=true,u=to[u];
	}
	assert(cnt[0][0]==cnt[1][1]);
	fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1;
	FOR(i,2,2*n){
		fac[i]=1ll*fac[i-1]*i%mod;
		inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
		invfac[i]=1ll*invfac[i-1]*inv[i]%mod;
	}
	S[0][0]=1;
	FOR(i,1,n) FOR(j,1,i) S[i][j]=(S[i-1][j-1]+1ll*(i-1)*S[i-1][j])%mod;
	int upr=cnt[0][0];
	FOR(i,0,upr) a[i]=1ll*fac[upr]*S[upr][i]%mod;
	upr=cnt[0][1];
	FOR(i,0,upr) FOR(j,0,i){
		if(cnt[0][0]) b[j]=(b[j]+1ll*C(upr,i)*fac[upr-i]%mod*C(upr-i+cnt[0][0]-1,upr-i)%mod*S[i][j])%mod;
		else if(i==upr) b[j]=(b[j]+1ll*C(upr,i)*fac[upr-i]%mod*S[i][j])%mod;
	}
	upr=cnt[1][0];
	FOR(i,0,upr) FOR(j,0,i){
		if(cnt[0][0]) c[j]=(c[j]+1ll*C(upr,i)*fac[upr-i]%mod*C(upr-i+cnt[0][0]-1,upr-i)%mod*S[i][j])%mod;
		else if(i==upr) c[j]=(c[j]+1ll*C(upr,i)*fac[upr-i]%mod*S[i][j])%mod;
	}
/*	printf("%d %d %d %d\n",cnt[0][0],cnt[0][1],cnt[1][0],cnt[1][1]);
	FOR(i,0,n) printf("%d ",a[i]);
	puts("");
	FOR(i,0,n) printf("%d ",b[i]);
	puts("");
	FOR(i,0,n) printf("%d ",c[i]);
	puts("");*/
	FOR(i,0,n) FOR(j,0,n-i) FOR(k,0,n-i-j) ans[i+j+k]=(ans[i+j+k]+1ll*a[i]*b[j]%mod*c[k])%mod;
	FOR(i,0,n-1) printf("%d ",n-i-tot>=0?ans[n-i-tot]:0);
}
posted @ 2020-08-05 21:30  ATS_nantf  阅读(197)  评论(0编辑  收藏  举报