1. 程式人生 > 其它 >[題解] 2021-2022 ICPC, NERC, Northern Eurasia Onsite - F Fancy Stack

[題解] 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;
}