【CF 715E】Complete the Permutations

题目

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

首先考虑对于两个给定的排列最小交换次数是多少,不难发现就是(n- ext{置换数}),置换数就是令(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}inom{k}{j}egin{bmatrix}j\iend{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;
}
原文地址:https://www.cnblogs.com/asuldb/p/12313631.html