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,n−1]) 的方案数。
n ≤ 250 n ≤ 250 n≤250,答案对 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 ai→bi。
记连边后的图上环的个数为 m m m,那么 a , b a,b a,b 的距离为 n − m n-m n−m。
回到原问题:
我们建 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 vai→pi。如果 b i ≠ 0 b_i≠0 bi=0,连边 p i → v b i p_i→v_{b_i} pi→vbi。得到一张初始的图。
在这张图中, v x → p y v_x→p_y vx→py 表示补全后的 a y = x a_y=x ay=x, p x → v y p_x→v_y px→vy 表示补全后的 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 类链组成。
- 仅 2 2 2 类链组成。
- 由 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 类链组成。
- 由 3 , 4 3,4 3,4 类链组成。
- 由 1 , 3 , 4 1,3,4 1,3,4 类链组成。
- 由 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 c1−j 条 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} Ac1−j+c3c1−j 种方案。
于是可得递推式:
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=0∑c1Cc1j×Sji×Ac1−j+c3c1−j
其中 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+1∑c1fj×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 类链) n → _n→ n→ 3 3 3 类链 → → → ( 2 2 2 类链) n _n n
- ( 1 1 1 类链) n → _n→ n→ 3 3 3 类链
- 3 3 3 类链 → → → ( 2 2 2 类链) n _n n
- 3 3 3 类链
发现这 4 4 4 种长链有两个共有的特点:
- 只含 1 1 1 条 3 3 3 类链。
- 开头和结尾一定都是 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 i−j 个环。
因为每条长链里必定只含 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=0∑ihj×Sc3i−j∗c4!
表示将每条长链先分别接 1 1 1 条 4 4 4 类链,然后摆成 i − j i-j i−j 个环。
摆环的方案数显然是 S c 3 i − j S_{c_3}^{i-j} Sc3i−j。
因为 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;
}