题解 —— CF1666F Fancy Stack
StillEmpty · · 题解
前言:这破题我whk时放在脑子里想,但是又不是全神贯注的想,想的时候整个人也很不在状态,就这样想了三天想出来了。好像我很弱,一道蓝色的题想了三天,但是我觉得我很强,因为没人教我,我靠自己硬想完全没受到提示自学了计数dp。如果你去看
我们设偶数块指一个编号为
我们首先观察一下本题有什么性质:奇数块小于相邻的两个偶数块,然而偶数块是严格上升的,所以只要保证每一个奇数块都小于上面的相邻的一个偶数块就行了。形式化的讲,对于
我们接着想,除了最下面的偶数块和最上面的偶数块,每个偶数块都要找一个比他小的数和他配对。最下面的偶数块不用配对。最上面的偶数块要找两个数配对。比如题目中的例子,四个偶数块是
虽然可能讲的有点啰嗦,但目前的推导都是十分
但凡你做过一两道 cnt[i] 就是
设
我们已经快做完了。需要注意的是,最上面的偶数块要找俩奇数块配对。根据做题经验,我们可能是要更改dp状态和转移式。但这个问题要通过改状态转移式解决很烦,作者可能不够聪明,花了很多时间想怎样改状态设计。我们根本没必要这么麻烦,直接枚举最上面的偶数块的
当然,我们每一步操作都要有紧密的逻辑性,如果你观察一下会发现我们删除
#include <bits/stdc++.h>
using namespace std;
typedef long long ll; typedef const int& cint;
const int N = 5000, MOD = 998244353;
int n, m;
int a[N+1], cnt[N+1], pcnt[N+1], f[N/2][N+1];
int fac[N+1], inv[N+1], invfac[N+1];
inline int add(cint a, cint b) {
return (a+b)%MOD;
}
inline int mul(cint a, cint b) {
return (ll)a*b%MOD;
}
void init() {
fac[0] = 1;
for(int i = 1; i <= N; ++i) fac[i] = mul(fac[i-1], i);
inv[1] = 1;
for(int i = 2; i <= N; ++i) {
inv[i] = mul((MOD-MOD/i), inv[MOD%i]);
}
invfac[1] = 1;
for(int i = 2; i <= N; ++i) {
invfac[i] = mul(invfac[i-1], inv[i]);
}
}
inline int C(cint n, cint m) {
if(m < 0 || n < 0 || m > n) return 0;
if(m == 0 || m == n) return 1;
return mul(fac[n], mul(invfac[m], invfac[n-m]));
}
void solve() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
sort(a+1, a+n+1, greater<int>());
if(a[1] == a[2]) {
printf("0\n"); return;
}
if(n == 2) {
printf("1\n"); return;
}
m = 0; memset(cnt, 0, sizeof(cnt)); memset(f, 0, sizeof(f));
for(int i = 2; i <= n; ++i) {
if(a[i] != a[i-1]) ++m;
++cnt[m];
}
for(int i = 1; i <= m; ++i) {
pcnt[i] = pcnt[i-1]+cnt[i];
}
f[0][0] = 1;
for(int i = 1; i < n/2-1; ++i) {
for(int j = i; j <= min(i*2, m-1); ++j) {
f[i][j] = add(mul(C(i-(pcnt[j-1]-i), cnt[j]), f[i][j-1]), mul(C((i-1)-(pcnt[j-1]-(i-1)), cnt[j]-1), f[i-1][j-1]));
}
}
int ans = 0;
for(int i = n/2-1; pcnt[m]-pcnt[i] >= 2; ++i) {
int tmp = 1;
for(int j = i+1; j <= m; ++j) {
tmp = mul(tmp, C(n/2-(pcnt[i]-(n/2-1))-(pcnt[j-1]-pcnt[i]), cnt[j]));
}
ans = add(ans, mul(mul(f[n/2-2][i-1], C(n/2-2-(pcnt[i-1]-(n/2-2)), cnt[i]-1)), tmp));
}
printf("%d\n", ans);
}
int main() {
// freopen("in", "r", stdin);
int t;
scanf("%d", &t); init();
while(t--) solve();
return 0;
}