[CF715E] Complete the Permutations

Problem

给定两个长度为 n n n 的排列 a , b a,b a,b,但是其中有些位置未知,用 0 0 0 表示。

定义两个排列的距离为:每次选择 a a a 中的两个元素交换,使其变为 a a a 的最小次数。

要求补全两个排列,求补全之后 a , b a,b a,b 距离为 i i i ( i ∈ [ 0 , n − 1 ] ) (i∈[0,n-1]) (i[0,n1]) 的方案数。

n ≤ 250 n ≤ 250 n250,答案对 998244353 998244353 998244353 取模。

Solution

先考虑怎么算补全之后 a , b a,b a,b 的距离:

对于每个 i i i ( i ∈ [ 1 , n ] ) (i∈[1,n]) (i[1,n]),连边 a i → b i a_i→b_i aibi

记连边后的图上环的个数为 m m m,那么 a , b a,b a,b 的距离为 n − m n-m nm

回到原问题:

我们建 n n n 个点 p 1 , p 2 , . . . , p n p_1,p_2,...,p_n p1,p2,...,pn 表示 n n n 个位置,建 n n n 个点 v 1 , v 2 , . . . , v n v_1,v_2,...,v_n v1,v2,...,vn 表示 1 1 1 ~ n n n n n n 个数值。

对于每个 i i i ( i ∈ [ 1 , n ] ) (i∈[1,n]) (i[1,n]),如果 a i ≠ 0 a_i≠0 ai=0,连边 v a i → p i v_{a_i}→p_i vaipi。如果 b i ≠ 0 b_i≠0 bi=0,连边 p i → v b i p_i→v_{b_i} pivbi。得到一张初始的图。

在这张图中, v x → p y v_x→p_y vxpy 表示补全后的 a y = x a_y=x ay=x p x → v y p_x→v_y pxvy 表示补全后的 b x = y b_x=y bx=y

只能在 v i v_i vi p j p_j pj 之间连边,不许 v i v_i vi v j v_j vj 连边,也不许 p i p_i pi p j p_j pj 连边。

初始的图中有一些环和链(包括单点成链)。

我们要做的就是加一些边(这些边只能从一条链的结尾连向一条链的开头,可以是同一条链的结尾和开头),使得最终的图有 x x x ( x ∈ [ 1 , n ] ) (x∈[1,n]) (x[1,n]) 个环, 0 0 0 条链。

记初始的图中:

环有 c 0 c_0 c0 个。
p p p 开头, v v v 结尾的链( 1 1 1 类链)有 c 1 c_1 c1 条。
v v v 开头, p p p 结尾的链( 2 2 2 类链)有 c 2 c_2 c2 条。
p p p 开头, p p p 结尾的链( 3 3 3 类链)有 c 3 c_3 c3 条。
v v v 开头, v v v 结尾的链( 4 4 4 类链)有 c 4 c_4 c4 条。

因为环上必须是 v , p v,p v,p 交替出现的,所以一个环的组成可以是:

  1. 1 1 1 类链组成。
  2. 2 2 2 类链组成。
  3. 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 类链组成。
  4. 3 , 4 3,4 3,4 类链组成。
  5. 1 , 3 , 4 1,3,4 1,3,4 类链组成。
  6. 2 , 3 , 4 2,3,4 2,3,4 类链组成。

我们先考虑 1 , 2 , 3 1,2,3 1,2,3 类链之间怎么连边。

f i f_i fi 表示满足 ⌈ \lceil i i i 个仅 1 1 1 类链组成的环 ⌋ \rfloor 的情况下, 1 1 1 类链的连边方案数。

因为 1 1 1 类链是 p p p 开头 v v v 结尾,所以我们考虑的 ⌈ \lceil 连边方案数 ⌋ \rfloor 也就是给每个 1 1 1 类链的结尾连一条出边的方案数。

显然这个出边要么连向 1 1 1 类链的开头,要么连向 3 3 3 类链的开头。

⌈ \lceil 恰好 i i i 个仅 1 1 1 类链组成的环 ⌋ \rfloor 的方案数不好算,考虑让 f i f_i fi 先表示 ⌈ \lceil 至少 i i i 个仅 1 1 1 类链组成的环 ⌋ \rfloor 的方案数。

枚举这 i i i ⌈ \lceil 1 1 1 类链组成的环 ⌋ \rfloor 用了 j j j 1 1 1 类链。

我们要从 c 1 c_1 c1 1 1 1 类链中选出 j j j 条,把它们排成 i i i 个环。

剩下的 c 1 − j c_1-j c1j 1 1 1 类链,要么连向 1 1 1 类链,要么连向 3 3 3 类链。显然是不可以连向用来成环的 j j j 1 1 1 类链的,那么就有 A c 1 − j + c 3 c 1 − j A_{c_1-j+c_3}^{c_1-j} Ac1j+c3c1j 种方案。

于是可得递推式:

f i = ∑ j = 0 c 1 C c 1 j × S j i × A c 1 − j + c 3 c 1 − j f_i=\sum_{j=0}^{c_1}C_{c_1}^j×S_j^i×A_{c_1-j+c_3}^{c_1-j} fi=j=0c1Cc1j×Sji×Ac1j+c3c1j

其中 C C C 是组合数, S S S 是第一类斯特林数, A A A 是排列数。

然后计算 ⌈ \lceil 恰好 i i i 个仅 1 1 1 类链组成的环 ⌋ \rfloor 的方案数:

f i − = ∑ j = i + 1 c 1 f j × C j i f_i-=\sum_{j=i+1}^{c_1}f_j×C_j^i fi=j=i+1c1fj×Cji

这样我们就把 1 1 1 类链的出边(结尾连出去的边)都搞定了。

接下来搞定 2 2 2 类链的入边。

1 1 1 类链同理,记 g i g_i gi 表示满足 ⌈ \lceil i i i 个仅 2 2 2 类链组成的环 ⌋ \rfloor 的情况下, 2 2 2 类链的连边方案数。跟 f i f_i fi 计算方法一样。

截至目前,除掉所有的环以及 4 4 4 类链,有下面 4 4 4 种长链(( x x x) n _n n 表示连续若干个 x x x):

  1. ( 1 1 1 类链) n → _n→ n 3 3 3 类链 → → ( 2 2 2 类链) n _n n
  2. ( 1 1 1 类链) n → _n→ n 3 3 3 类链
  3. 3 3 3 类链 → → ( 2 2 2 类链) n _n n
  4. 3 3 3 类链

发现这 4 4 4 种长链有两个共有的特点:

  1. 只含 1 1 1 3 3 3 类链。
  2. 开头和结尾一定都是 p p p

把这 4 4 4 种长链和所有的 4 4 4 类链一起串成环,我们就完成任务了。

我们令 h = f × g h=f×g h=f×g

a n s i ans_i ansi 表示最终的图有 i i i 个环的方案数。

枚举仅由 1 1 1 类链组成的环、仅由 2 2 2 类链组成的环共 j j j 个,那么我们要把上述 4 4 4 种长链和所有的 4 4 4 类链串成 i − j i-j ij 个环。

因为每条长链里必定只含 1 1 1 3 3 3 类链,所以我们可以给每条长链分别编号 1 1 1 ~ c 3 c_3 c3

显然最终形成的环一定是长链和 4 4 4 类链交替出现。

那么:
a n s i = ∑ j = 0 i h j × S c 3 i − j ∗ c 4 ! ans_i=\sum_{j=0}^ih_j×S_{c_3}^{i-j}*c_4! ansi=j=0ihj×Sc3ijc4!

表示将每条长链先分别接 1 1 1 4 4 4 类链,然后摆成 i − j i-j ij 个环。

摆环的方案数显然是 S c 3 i − j S_{c_3}^{i-j} Sc3ij

因为 c 3 = c 4 c_3=c_4 c3=c4,所以长链和 4 4 4 类链的连接方案数为 c 4 ! c_4! c4!

时间复杂度 o ( n 2 ) o(n^2) o(n2)

#include <bits/stdc++.h>

using namespace std;

#define ll long long

template <class t>
inline void read(t & res)
{
	char ch;
	while (ch = getchar(), !isdigit(ch));
	res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
	res = res * 10 + (ch ^ 48);
}

const int e = 2005, mod = 998244353;
int S[e][e], nxt[e], C[e][e], A[e][e], a[e], b[e], n, ans[e], f[e], g[e], h[e], fac[e];
int c0, c1, c2, c3, c4, deg[e], ret[e];
bool vis[e];

inline int plu(int x, int y)
{
	(x += y) >= mod && (x -= mod);
	return x;
}

inline int sub(int x, int y)
{
	(x -= y) < 0 && (x += mod);
	return x;
}

inline void dfs(int x, bool s, bool t)
{
	vis[x] = 1;
	int y = nxt[x];
	if (y)
	{
		if (vis[y]) c0++;
		else dfs(y, s, t ^ 1);
	}
	else
	{
		if (!s && t) c1++;
		else if (s && !t) c2++;
		else if (s && t) c3++;
		else c4++;
	}
}

inline void init(int cnt, int *f)
{
	int i, j;
	for (i = 0; i <= cnt; i++)
	for (j = 0; j <= cnt; j++)
	f[i] = (f[i] + (ll)C[cnt][j] * S[j][i] % mod * A[cnt - j + c3][cnt - j]) % mod;
	for (i = cnt; i >= 0; i--)
	for (j = i + 1; j <= cnt; j++)
	f[i] = sub(f[i], (ll)f[j] * C[j][i] % mod); 
}

int main()
{
	int i, j;
	read(n);
	for (i = 1; i <= n; i++) read(a[i]);
	for (i = 1; i <= n; i++) read(b[i]);
	for (i = 1; i <= n; i++)
	{
		if (a[i]) nxt[a[i] + n] = i, deg[i]++;
		if (b[i]) nxt[i] = b[i] + n, deg[b[i] + n]++;
	}
	for (i = 1; i <= 2 * n; i++)
	if (!vis[i] && !deg[i]) dfs(i, i > n, i > n);
	for (i = 1; i <= 2 * n; i++)
	if (!vis[i]) dfs(i, i > n, i > n);
	C[0][0] = A[0][0] = S[0][0] = fac[0] = 1;
	for (i = 1; i <= n; i++)
	{
		C[i][0] = A[i][0] = 1;
		fac[i] = (ll)fac[i - 1] * i % mod;
		for (j = 1; j <= i; j++)
		{
			C[i][j] = plu(C[i - 1][j - 1], C[i - 1][j]);
			A[i][j] = (A[i - 1][j] + (ll)A[i - 1][j - 1] * j) % mod;
			S[i][j] = (S[i - 1][j - 1] + (ll)S[i - 1][j] * (i - 1)) % mod;
		}
	}
	init(c1, f); init(c2, g);
	for (i = 0; i <= n; i++)
	for (j = 0; j <= i; j++)
	h[i] = (h[i] + (ll)f[j] * g[i - j]) % mod;
	for (i = 0; i <= n; i++)
	for (j = 0; j <= i; j++)
	ans[i] = (ans[i] + (ll)h[j] * S[c3][i - j] % mod * fac[c4]) % mod;
	for (i = 0; i < n; i++) 
	if (n - i - c0 >= 0) ret[i] = ans[n - i - c0];
	else ret[i] = 0;
	for (i = 0; i < n - 1; i++) printf("%d ", ret[i]);
	printf("%d\n", ret[n - 1]);
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值