[题解] 2021-2022 ICPC, NERC, Northern Eurasia Onsite - F Fancy Stack

给你一个有 \(n\) 个数的单调不降的序列 \(a\),让这个序列重新排列为 \(b\)。使得对于所有偶数位有 \(b_2 < b_4 < ... < b_{n-2} < b_n\),对于所有奇数位 \(i\)\(b_i < b_{i-1}, b_{i} < b_{i+1}\),注意是严格小于。

求合法 \(b\) 排列的方案数。对 \(998244353\) 取模,保证 \(n\) 是偶数。

赛时想了很长时间都不会,可能是我傻逼了。lzy 写了写 WA6,可惜最后没调出来。

下面的做法是翻译的题解。

先考虑所有数都不相同的情况。

\(f_{i,j}\) 表示考虑了最大的 \(i\) 个数,有 \(j\) 个数放在了偶数位上。

现在考虑第 \(i+1\) 个数。

  • 如果放在偶数位上 \(f_{i+1,j+1} \gets f_{i,j}\)
  • 如果放在奇数位上 \(f_{i+1,j} \gets f_{i,j} \times (\max(j-1,0) + [j=\frac{n}{2}] - (i-j))\)

当然这是不相同的情况。

如果存在数相同的话,可以把相同的数合并成一个,转移的时候枚举几个数作为偶数位,然后剩下的几个数放在奇数位上的方案数直接用组合数计算即可。 上面的这个是错的,因为要保证 \(b_{2} < b_4 < b_6 < ... < b_n\),所以相同的数中只能有 \(0\)\(1\) 个数放在偶数位,不过算贡献的时候确实需要转化成组合数统计。

总复杂度 \(\mathcal O(n^2)\)

理论存在,开写!

现在是 15:11 我看我什么时候写完。

现在是 16:45 我写完了,调了 \(\infty\) 时间的缘故是多测没清干净 /px

#include<bits/stdc++.h>
#define LL long long
#define int long long
#define orz cout << "tyy YYDS!!!\n"
using namespace std;
const int MAXN = 5e3 + 10;
const int INF = 1e9 + 7;
const int mod = 998244353;

int n, m;
int fac[MAXN], inv[MAXN];
int a[MAXN], b[MAXN], cnt[MAXN], s[MAXN];
int f[MAXN][MAXN];

int read() {
	int s = 0, f = 0; char ch = getchar();
	while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void Init(int M) {
	fac[0] = fac[1] = inv[0] = inv[1] = 1;
	for(int i = 2; i <= M; ++i) {
		fac[i] = fac[i - 1] * i % mod;
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	}
	for(int i = 2; i <= M; ++i) inv[i] = inv[i - 1] * inv[i] % mod;
}

int C(int n, int m) { return (n < 0 || n < m) ? 0 : fac[n] * inv[m] % mod * inv[n - m] % mod; }

void Main() {
	n = read(), m = 0;
	for(int i = 0; i <= n + 1; ++i) a[i] = b[i] = cnt[i] = s[i] = 0;
	for(int i = 1; i <= n; ++i) a[i] = read();
	for(int i = 1; i <= n; ++i) {
		if(a[i] != a[i - 1]) b[++m] = a[i], cnt[m] = 1;
		else cnt[m] ++;
	}
	for(int i = m; i >= 1; --i) s[i] = s[i + 1] + cnt[i];
	for(int i = 0; i <= n + 1; ++i) for(int j = 0; j <= n + 1; ++j) f[i][j] = 0;
	f[m + 1][0] = 1;
	for(int i = m + 1, M = n / 2; i > 1; --i) { // 枚举已经填了哪一位,接下来要填的这一位是 i-1 
		for(int j = 0; j <= M; ++j) { // 枚举已经放了几个偶数位 
			for(int k = cnt[i - 1] - 1; k <= cnt[i - 1]; ++k) { // 枚举拿几个放在奇数位,奇数位应该放 (max(j-1,0)+(j==M)) 个,已经放了 (s[i] - j) 个 
				if(j + cnt[i - 1] - k <= M) {
                    f[i - 1][j + (cnt[i - 1] - k)] = (f[i - 1][j + (cnt[i - 1] - k)] + f[i][j] * C(max(j - 1, 0ll) + (j == M) - (s[i] - j), k) % mod) % mod;
                }
			}
		}
	}
	printf("%lld\n", f[1][n/2]);
}

signed main() {
	Init(5000);
	int T = read();
	while(T--) Main();
	return 0;
}
posted @ 2022-04-14 16:57  Suzt_ilymtics  阅读(326)  评论(0编辑  收藏  举报