【校內訓練2018-10-19】Gift
阿新 • • 發佈:2018-11-08
【思路要點】
- 首先,若不存在 ,將 連向 會形成一個置換,令該置換環的個數為 ,交換步數即為 ,因此,我們本質上需要計算形成 個置換環的方案數 。
- 題目中已經給出了圖中的若干條邊,它們會形成一些路徑和一些環,對於已經形成的環,我們只需要在輸出答案的時候考慮即可,接下來我們考慮路徑。
- 一條路徑可以是如下的若干種形式: 其中 代表路徑的開頭或結尾為 , 代表路徑的開頭或結尾為一個固定的數字。其中 型的路徑實際上等價於 型的路徑。我們記 表示 型路徑的個數, 表示 型路徑的個數, 表示 型路徑的個數。
- 我們接下來要做的是對所有 標號,並且計算形成 個環的方案數。首先, 型的路徑存在排列先後順序問題,我們將所有沒有出現過的 個數按序填入所有 的入點,然後將 型的路徑看做 型的路徑,並在最後將答案乘以 。
- 將 個 型路徑串聯為 個環的方案數為第一類斯特林數 ,接下來我們要將 個 型的路徑插入到上面的方案中。
- 記 表示插入了 個 型的路徑後形成了 個環的方案數,有 。那麼 。
- 時間複雜度 。
【程式碼】
#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; }