[題解] 2021-2022 ICPC, NERC, Northern Eurasia Onsite - F Fancy Stack
阿新 • • 發佈:2022-04-14
給你一個有 \(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+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\)
總複雜度 \(\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; }