1. 程式人生 > >【校內訓練2018-10-19】Gift

【校內訓練2018-10-19】Gift

【思路要點】

  • 首先,若不存在 0 0 ,將 a i
    a_i
    連向 b i b_i 會形成一個置換,令該置換環的個數為 c
    n t cnt
    ,交換步數即為 N c n
    t N-cnt
    ,因此,我們本質上需要計算形成 i i 個置換環的方案數 a n s i ans_i
  • 題目中已經給出了圖中的若干條邊,它們會形成一些路徑和一些環,對於已經形成的環,我們只需要在輸出答案的時候考慮即可,接下來我們考慮路徑。
  • 一條路徑可以是如下的若干種形式: 0 0 , 0 x , x 0 , x x 0-0,0-x,x-0,x-x 其中 0 0 代表路徑的開頭或結尾為 0 0 x x 代表路徑的開頭或結尾為一個固定的數字。其中 x x x-x 型的路徑實際上等價於 0 0 0-0 型的路徑。我們記 c n t 0 cnt_0 表示 0 0 , x x 0-0,x-x 型路徑的個數, c n t 1 cnt_1 表示 x 0 x-0 型路徑的個數, c n t 2 cnt_2 表示 0 x 0-x 型路徑的個數。
  • 我們接下來要做的是對所有 0 0 標號,並且計算形成 i i 個環的方案數。首先, 0 0 0-0 型的路徑存在排列先後順序問題,我們將所有沒有出現過的 c n t 0 cnt_0 個數按序填入所有 0 0 0-0 的入點,然後將 0 0 0-0 型的路徑看做 x 0 x-0 型的路徑,並在最後將答案乘以 c n t 0 ! cnt_0!
  • c n t 0 + c n t 1 cnt_0+cnt_1 x 0 x-0 型路徑串聯為 i i 個環的方案數為第一類斯特林數 s ( c n t 0 + c n t 1 , i ) s(cnt_0+cnt_1,i) ,接下來我們要將 c n t 2 cnt_2 0 x 0-x 型的路徑插入到上面的方案中。
  • d p i , j dp_{i,j} 表示插入了 i i 0 x 0-x 型的路徑後形成了 j j 個環的方案數,有 d p i , j = d p i 1 , j 1 + ( c n t 0 + i 1 ) d p i 1 , j dp_{i,j}=dp_{i-1,j-1}+(cnt_0+i-1)*dp_{i-1,j} 。那麼 a n s i = d p c n t 2 , i ans_i=dp_{cnt_2,i}
  • 時間複雜度 O ( N 2 ) O(N^2)

【程式碼】

#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;
}