【CF 715E】Complete the Permutations

题目

自闭了一晚上,终于看懂题解在说啥了;

首先考虑对于两个给定的排列最小交换次数是多少,不难发现就是\(n-\text{置换数}\),置换数就是令\(a_i->b_i\)后图中的环的个数,每一个置换也就是环可以少交换一次;

对于这道题,还是令\(a_i\)\(b_i\)连边,我们用\(0\)来表示未知数,这样整个图中大概就有怎么情况

  • 已知边形成的环

  • \(0->x_1->x_2->x_3->\dots x_n\)

  • \(x_1->x_2->x_3->\dots x_n->0\)

  • \(0->x_1->x_2->x_3->\dots x_n->0\)

首先已经形成的环就没啥影响了,对于后三种情况,我们把\(x_i->x_{i+1}\)这类边缩掉,于是整个图中就只剩下\(x->0,0->x,0->0\)这三种边,考虑这些边的成环情况;

不难发现\(x->0,0->y\)是不能成环的,因为\(x\)没有入度说明在\(B\)中没有出现,\(y\)没有入度说明在\(B\)中出现;而\(x->0\)成环本质上就是使得\(0\)成为\(x\)

考虑\(x->0\)边的成环情况,设\(g_i\)表示至少形成了\(i\)个环,设有\(k\)\(x->0\)边,\(h\)\(0->0\)边,于是有

\[g_i=\sum_{j=i}^{k}\binom{k}{j}\begin{bmatrix}j\\i\end{bmatrix}(h+k-j)^{\underline {k-j}} \]

枚举成环的边有\(j\)条,从一共\(k\)条边里选\(j\)条边出来,之后分到\(i\)个环中去,对于剩下的\(k-j\)条边每条边找一条\(0->0\)边或不在环中的\(x->0\)边连过去,所以是一个下降幂。注意连到\(0->0\)边是实际上没啥影响,\(0->0\)边仍然是\(0->0\)边,就是延长了一下。

\(0->x\)边同理;我们求出\(g\)后来个二项式反演,就是求出恰好的情况了。我们把这两种边求出的结果卷积一波,就是\(x\)环的情况了;对于\(0->0\)边也能成环,成环情况就是第一类斯特林数,于是再和第一类斯特林数卷积一下;同时由于\(0->0\)边的的顺序可以自由钦定,所以还需要乘上\(h!\)

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int mod=998244353;const int maxn=255;
inline int dqm(int x) {return x<0?x+mod:x;}
inline int qm(int x) {return x>=mod?x-mod:x;}
inline int ksm(int a,int b) {
	int S=1;for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)S=1ll*S*a%mod;return S;
}
struct E{int v,nxt;}e[maxn];
int cnt[2],h,num,tot,n,vis[maxn],vit[maxn],in[maxn],out[maxn];
int fac[maxn],a[maxn],ifac[maxn],b[maxn],S[maxn][maxn],head[maxn],g[4][maxn];
inline int C(int n,int m) {
	return m>n?0:1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
inline int A(int n,int m) {
	return m>n?0:1ll*fac[n]*ifac[n-m]%mod;
}
inline void add(int x,int y){
	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void dfs1(int x) {
	if(x==0&&vis[x]) {++h;return;}
	if(!head[x]&&x) {++cnt[1];return;}
	if(vis[x])return;vis[x]=1;
	for(re int i=head[x];i;i=e[i].nxt)dfs1(e[i].v);
}
void dfs2(int x) {
	if(!head[x]&&x) {++cnt[0];return;}
	if(vit[x])return;vit[x]=1;
	for(re int i=head[x];i;i=e[i].nxt)dfs2(e[i].v);
}
int dfs3(int x,int st) {
	if(x==st&&vis[st]) return 1;
	if(vis[x]) return 0;vis[x]=1;
	for(re int i=head[x];i;i=e[i].nxt)return dfs3(e[i].v,st);
	return 0;
}
inline void solve(int *f,int k) {
	for(re int i=0;i<=k;i++)
		for(re int j=i;j<=k;j++) 
			f[i]=qm(f[i]+1ll*C(k,j)*S[j][i]%mod*A(h+k-j,k-j)%mod);
	for(re int nw=0,i=0;i<=k;f[i]=nw,nw=0,++i) 
		for(re int j=i;j<=n;j++) {
			if((j-i)&1) nw=dqm(nw-1ll*C(j,i)*f[j]%mod);
			else nw=qm(nw+1ll*C(j,i)*f[j]%mod);
		}
}
int main() {
	n=read();
	for(re int i=1;i<=n;i++)a[i]=read();
	for(re int i=1;i<=n;i++)b[i]=read();
	fac[0]=ifac[0]=1;
	for(re int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
	ifac[n]=ksm(fac[n],mod-2);
	for(re int i=n-1;i;i--)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
	S[0][0]=1;
	for(re int i=1;i<=n;i++)
		for(re int j=1;j<=i;j++)
			S[i][j]=qm(S[i-1][j-1]+1ll*(i-1)*S[i-1][j]%mod);
	for(re int i=1;i<=n;i++)add(a[i],b[i]);dfs1(0);
	num=0;memset(head,0,sizeof(head));
	for(re int i=1;i<=n;i++)add(b[i],a[i]);dfs2(0);
	for(re int i=1;i<=n;i++)in[a[i]]++,out[b[i]]++,vis[i]|=vit[i];
	for(re int i=1;i<=n;i++)if(in[i]==out[i]&&in[i]==1&&!vis[i])tot+=dfs3(i,i);
	solve(g[0],cnt[0]);solve(g[1],cnt[1]);
	for(re int i=0;i<=n;i++)
		for(re int j=i;j>=0;j--) g[2][i]=qm(g[2][i]+1ll*g[0][j]*g[1][i-j]%mod);
	for(re int i=0;i<=n;i++) 
		for(re int j=0;j<=i;j++) g[3][i]=qm(g[3][i]+1ll*g[2][j]*S[h][i-j]%mod);
	for(re int i=0;i<=n;i++) g[3][i]=1ll*g[3][i]*fac[h]%mod;
	for(re int i=0;i<n;i++) printf("%d ",n-i-tot>=0?g[3][n-i-tot]:0);
	return 0;
}
posted @ 2020-02-15 20:56  asuldb  阅读(235)  评论(0编辑  收藏  举报