版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/83175844
【思路要点】
- 首先,若不存在 ,将 连向 会形成一个置换,令该置换环的个数为 ,交换步数即为 ,因此,我们本质上需要计算形成 个置换环的方案数 。
- 题目中已经给出了图中的若干条边,它们会形成一些路径和一些环,对于已经形成的环,我们只需要在输出答案的时候考虑即可,接下来我们考虑路径。
- 一条路径可以是如下的若干种形式: 其中 代表路径的开头或结尾为 , 代表路径的开头或结尾为一个固定的数字。其中 型的路径实际上等价于 型的路径。我们记 表示 型路径的个数, 表示 型路径的个数, 表示 型路径的个数。
- 我们接下来要做的是对所有 标号,并且计算形成 个环的方案数。首先, 型的路径存在排列先后顺序问题,我们将所有没有出现过的 个数按序填入所有 的入点,然后将 型的路径看做 型的路径,并在最后将答案乘以 。
- 将 个 型路径串联为 个环的方案数为第一类斯特林数 ,接下来我们要将 个 型的路径插入到上面的方案中。
- 记 表示插入了 个 型的路径后形成了 个环的方案数,有 。那么 。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2005; const int P = 998244353; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, a[MAXN], b[MAXN], c[MAXN]; int out[MAXN], in[MAXN], dp[MAXN][MAXN], s[MAXN][MAXN]; int fac[MAXN], inv[MAXN], ans[MAXN]; int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } int getc(int x, int y) { if (y > x) return 0; else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P; } void init(int n) { fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % P; inv[n] = power(fac[n], P - 2); for (int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1ll) % P; } void update(int &x, int y) { x += y; if (x >= P) x -= P; } int main() { freopen("gift.in", "r", stdin); freopen("gift.out", "w", stdout); read(n), init(n); for (int i = 1; i <= n; i++) read(a[i]); for (int i = 1; i <= n; i++) read(b[i]); memset(in, -1, sizeof(in)); memset(out, -1, sizeof(out)); for (int i = 1; i <= n; i++) { if (a[i] != 0) out[a[i]] = b[i]; if (b[i] != 0) in[b[i]] = a[i]; } static bool vis[MAXN]; int cnt[4] = {0, 0, 0, 0}, loop = 0; for (int i = 1; i <= n; i++) { if (in[i] > 0) continue; int pos = i; while (pos > 0 && !vis[pos]) { vis[pos] = true; if (in[i] == -1 && out[pos] == -1) cnt[0]++; else if (in[i] == -1 && out[pos] == 0) cnt[1]++; else if (in[i] == 0 && out[pos] == -1) cnt[2]++; else if (in[i] == 0 && out[pos] == 0) cnt[3]++; else pos = out[pos]; } } for (int i = 1; i <= n; i++) if (!vis[i]) { loop++; int pos = i; while (!vis[pos]) { vis[pos] = true; pos = out[pos]; } } s[0][0] = 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) s[i][j] = (s[i - 1][j - 1] + s[i - 1][j] * (i - 1ll)) % P; for (int i = 0; i <= n; i++) dp[0][i] = s[cnt[0] + cnt[1]][i]; for (int i = 1; i <= cnt[2]; i++) for (int j = 1; j <= cnt[0] + cnt[1] + i; j++) dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j] * (cnt[0] + i - 1ll)) % P; int empty = 0; for (int i = 1; i <= n; i++) if (a[i] + b[i] == 0) empty++; static int ans[MAXN]; for (int i = 1; i <= n + 1; i++) ans[i] = 1ll * dp[cnt[2]][n - i + 1] * fac[cnt[0]] % P; for (int i = 1; i <= n; i++) if (i + loop <= n + 1) printf("%d ", ans[i + loop]); else printf("%d ", 0); return 0; }