[CF715E] Complete the Permutations(dp+组合计数)
Posted cyf32768
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CF715E] Complete the Permutations(dp+组合计数)相关的知识,希望对你有一定的参考价值。
Problem
给定两个长度为 (n) 的排列 (a,b),但是其中有些位置未知,用 (0) 表示。
定义两个排列的距离为:每次选择 (a) 中的两个元素交换,使其变为 (a) 的最小次数。
要求补全两个排列,求补全之后 (a,b) 距离为 (i) ((i∈[0,n-1])) 的方案数。
(n ≤ 250),答案对 (998244353) 取模。
Solution
先考虑怎么算补全之后 (a,b) 的距离:
对于每个 (i) ((i∈[1,n])),连边 (a_i→b_i)。
记连边后的图上环的个数为 (m),那么 (a,b) 的距离为 (n-m)。
回到原问题:
我们建 (n) 个点 (p_1,p_2,...,p_n) 表示 (n) 个位置,建 (n) 个点 (v_1,v_2,...,v_n) 表示 (1) ~ (n) 这 (n) 个数值。
对于每个 (i) ((i∈[1,n])),如果 (a_i≠0),连边 (v_{a_i}→p_i)。如果 (b_i≠0),连边 (p_i→v_{b_i})。得到一张初始的图。
在这张图中,(v_x→p_y) 表示补全后的 (a_y=x),(p_x→v_y) 表示补全后的 (b_x=y)。
只能在 (v_i) 和 (p_j) 之间连边,不许 (v_i) 和 (v_j) 连边,也不许 (p_i) 和 (p_j) 连边。
初始的图中有一些环和链(包括单点成链)。
我们要做的就是加一些边(这些边只能从一条链的结尾连向一条链的开头,可以是同一条链的结尾和开头),使得最终的图有 (x) ((x∈[1,n])) 个环,(0) 条链。
记初始的图中:
环有 (c_0) 个。
以 (p) 开头,(v) 结尾的链((1) 类链)有 (c_1) 条。
以 (v) 开头,(p) 结尾的链((2) 类链)有 (c_2) 条。
以 (p) 开头,(p) 结尾的链((3) 类链)有 (c_3) 条。
以 (v) 开头,(v) 结尾的链((4) 类链)有 (c_4) 条。
因为环上必须是 (v,p) 交替出现的,所以一个环的组成可以是:
- 仅 (1) 类链组成。
- 仅 (2) 类链组成。
- 由 (1,2,3,4) 类链组成。
- 由 (3,4) 类链组成。
- 由 (1,3,4) 类链组成。
- 由 (2,3,4) 类链组成。
我们先考虑 (1,2,3) 类链之间怎么连边。
记 (f_i) 表示满足 (lceil) 有 (i) 个仅 (1) 类链组成的环 ( floor) 的情况下,(1) 类链的连边方案数。
因为 (1) 类链是 (p) 开头 (v) 结尾,所以我们考虑的 (lceil) 连边方案数 ( floor) 也就是给每个 (1) 类链的结尾连一条出边的方案数。
显然这个出边要么连向 (1) 类链的开头,要么连向 (3) 类链的开头。
(lceil) 恰好有 (i) 个仅 (1) 类链组成的环 ( floor) 的方案数不好算,考虑让 (f_i) 先表示 (lceil) 至少有 (i) 个仅 (1) 类链组成的环 ( floor) 的方案数。
枚举这 (i) 个 (lceil) 仅 (1) 类链组成的环 ( floor) 用了 (j) 条 (1) 类链。
我们要从 (c_1) 条 (1) 类链中选出 (j) 条,把它们排成 (i) 个环。
剩下的 (c_1-j) 条 (1) 类链,要么连向 (1) 类链,要么连向 (3) 类链。显然是不可以连向用来成环的 (j) 条 (1) 类链的,那么就有 (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}]
其中 (C) 是组合数,(S) 是第一类斯特林数,(A) 是排列数。
然后计算(lceil) 恰好有 (i) 个仅 (1) 类链组成的环 ( floor) 的方案数:
[f_i-=sum_{j=i+1}^{c_1}f_j×C_j^i]
这样我们就把 (1) 类链的出边(结尾连出去的边)都搞定了。
接下来搞定 (2) 类链的入边。
和 (1) 类链同理,记 (g_i) 表示满足 (lceil) 有 (i) 个仅 (2) 类链组成的环 ( floor) 的情况下,(2) 类链的连边方案数。跟 (f_i) 计算方法一样。
截至目前,除掉所有的环以及 (4) 类链,有下面 (4) 种长链(((x))(_n) 表示连续若干个 (x)):
- ((1) 类链)(_n→) (3) 类链 (→) ((2) 类链)(_n)
- ((1) 类链)(_n→) (3) 类链
- (3) 类链 (→) ((2) 类链)(_n)
- (3) 类链
发现这 (4) 种长链有两个共有的特点:
- 只含 (1) 条 (3) 类链。
- 开头和结尾一定都是 (p)。
把这 (4) 种长链和所有的 (4) 类链一起串成环,我们就完成任务了。
我们令 (h=f×g)。
记 (ans_i) 表示最终的图有 (i) 个环的方案数。
枚举仅由 (1) 类链组成的环、仅由 (2) 类链组成的环共 (j) 个,那么我们要把上述 (4) 种长链和所有的 (4) 类链串成 (i-j) 个环。
因为每条长链里必定只含 (1) 条 (3) 类链,所以我们可以给每条长链分别编号 (1) ~ (c_3)。
显然最终形成的环一定是长链和 (4) 类链交替出现。
那么:
[ans_i=sum_{j=0}^ih_j×S_{c_3}^{i-j}*c_4!]
表示将每条长链先分别接 (1) 条 (4) 类链,然后摆成 (i-j) 个环。
摆环的方案数显然是 (S_{c_3}^{i-j})。
因为 (c_3=c_4),所以长链和 (4) 类链的连接方案数为 (c_4!)。
时间复杂度 (o(n^2))。
#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
", ret[n - 1]);
return 0;
}
以上是关于[CF715E] Complete the Permutations(dp+组合计数)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces 1203F2 Complete the Projects (hard version)
codeforces 372 div2 Complete the Word 716B