【校内训练2018-10-19】Gift

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/83175844

【思路要点】

  • 首先,若不存在 00 ,将 aia_i 连向 bib_i 会形成一个置换,令该置换环的个数为 cntcnt ,交换步数即为 NcntN-cnt ,因此,我们本质上需要计算形成 ii 个置换环的方案数 ansians_i
  • 题目中已经给出了图中的若干条边,它们会形成一些路径和一些环,对于已经形成的环,我们只需要在输出答案的时候考虑即可,接下来我们考虑路径。
  • 一条路径可以是如下的若干种形式: 00,0x,x0,xx0-0,0-x,x-0,x-x 其中 00 代表路径的开头或结尾为 00xx 代表路径的开头或结尾为一个固定的数字。其中 xxx-x 型的路径实际上等价于 000-0 型的路径。我们记 cnt0cnt_0 表示 00,xx0-0,x-x 型路径的个数, cnt1cnt_1 表示 x0x-0 型路径的个数, cnt2cnt_2 表示 0x0-x 型路径的个数。
  • 我们接下来要做的是对所有 00 标号,并且计算形成 ii 个环的方案数。首先, 000-0 型的路径存在排列先后顺序问题,我们将所有没有出现过的 cnt0cnt_0 个数按序填入所有 000-0 的入点,然后将 000-0 型的路径看做 x0x-0 型的路径,并在最后将答案乘以 cnt0!cnt_0!
  • cnt0+cnt1cnt_0+cnt_1x0x-0 型路径串联为 ii 个环的方案数为第一类斯特林数 s(cnt0+cnt1,i)s(cnt_0+cnt_1,i) ,接下来我们要将 cnt2cnt_20x0-x 型的路径插入到上面的方案中。
  • dpi,jdp_{i,j} 表示插入了 ii0x0-x 型的路径后形成了 jj 个环的方案数,有 dpi,j=dpi1,j1+(cnt0+i1)dpi1,jdp_{i,j}=dp_{i-1,j-1}+(cnt_0+i-1)*dp_{i-1,j} 。那么 ansi=dpcnt2,ians_i=dp_{cnt_2,i}
  • 时间复杂度 O(N2)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;
}
阅读更多
换一批

没有更多推荐了,返回首页